Snap for 10130956 from 2e5bc64514 to udc-qpr1-release
Change-Id: I5ab8f9dedbfd4e777935f7c02a592363c688d203
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?androidprv:attr/materialColorPrimaryContainer">
|
||||
android:tint="?android:attr/colorPrimary">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10c5.52,0 10,-4.48 10,-10S17.52,2 12,2zM10.59,16.6l-4.24,-4.24l1.41,-1.41l2.83,2.83l5.66,-5.66l1.41,1.41L10.59,16.6z"/>
|
||||
|
||||
22
res/layout/preference_external_action_icon.xml
Normal file
22
res/layout/preference_external_action_icon.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<ImageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_chevron_right_24dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp" />
|
||||
@@ -129,7 +129,9 @@
|
||||
<!-- Title for all hearing devices related controls section. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_device_controls_general">For all available hearing devices</string>
|
||||
<!-- Connected devices settings. Title of the preference to show the entrance of the hearing device controls related page. [CHAR LIMIT=65] -->
|
||||
<string name="bluetooth_device_controls_title">Shortcuts & hearing aid compatibility</string>
|
||||
<string name="bluetooth_device_controls_title">Hearing device settings</string>
|
||||
<!-- Connected devices settings. Title of the preference to show the entrance of the hearing device controls related page. [CHAR LIMIT=65] -->
|
||||
<string name="bluetooth_device_controls_summary">Audio output, shortcut, hearing aid compatibility</string>
|
||||
<!-- Title for this device specific controls section. [CHAR LIMIT=30] -->
|
||||
<string name="bluetooth_device_controls_specific">For this device</string>
|
||||
<!-- Connected devices settings. Title of the preference to show the entrance of the audio output page. It can change different types of audio are played on phone or other bluetooth devices. [CHAR LIMIT=35] -->
|
||||
@@ -5877,12 +5879,8 @@
|
||||
<string name="add_account_label">Add account</string>
|
||||
<!-- Label for the state of the work profile [CHAR LIMIT=80] -->
|
||||
<string name="managed_profile_not_available_label">Work profile isn\u2019t available yet</string>
|
||||
<!-- This string is the title of a setting. If a user taps the setting, they can turn their work profile on or off. The work profile is a section of their phone that's managed by their employer. "Work" is an adjective. -->
|
||||
<string name="work_mode_label">Work profile</string>
|
||||
<!-- This string is located under a setting and describes what the setting does. It's letting a user know whether their work profile is on or off, and they can use the setting to turn it on or off. The work profile is a section of their phone that's managed by their employer. "Work" is an adjective.-->
|
||||
<string name="work_mode_on_summary">Managed by your organization</string>
|
||||
<!-- This string is located under a setting and describes what the setting does. It's letting a user know whether their work profile is on or off, and they can use the setting to turn it on or off. The work profile is a section of their phone that's managed by their employer. "Work" is an adjective.-->
|
||||
<string name="work_mode_off_summary">Apps and notifications are off</string>
|
||||
<!-- This string is the title of a setting. If a user taps the setting, they can turn their work apps on or off. The work apps are a group of apps that are managed by the the user's employer. While this setting is off, the user cannot interact with those apps or get notifications from them. "Work" is an adjective. -->
|
||||
<string name="work_mode_label">Work apps</string>
|
||||
<!-- Button label to remove the work profile [CHAR LIMIT=35] -->
|
||||
<string name="remove_managed_profile_label">Remove work profile</string>
|
||||
<!-- Data synchronization settings screen, title of setting that controls whether background data should be used [CHAR LIMIT=30] -->
|
||||
@@ -7175,7 +7173,7 @@
|
||||
<string name="docking_sounds_title">Docking sounds</string>
|
||||
|
||||
<!-- Sound: Other sounds: Title for the option enabling touch sounds. [CHAR LIMIT=30] -->
|
||||
<string name="touch_sounds_title">Touch sounds</string>
|
||||
<string name="touch_sounds_title">Tap & click sounds</string>
|
||||
|
||||
<!-- Sound: Other sounds: Title for the option enabling the vibrate icon. [CHAR LIMIT=50] -->
|
||||
<string name="vibrate_icon_title">Always show icon when in vibrate mode</string>
|
||||
@@ -9783,6 +9781,8 @@
|
||||
<string name="cross_profile_calendar_title">Cross-profile calendar</string>
|
||||
<!-- [CHAR LIMIT=NONE] Setting description. If the user turns on this setting, they can see their work events on their personal calendar. -->
|
||||
<string name="cross_profile_calendar_summary">Show work events on your personal calendar</string>
|
||||
<!-- [CHAR_LIMIT_NONE] Footer description. Explains to the user what will happen when work apps are turned off. -->
|
||||
<string name="managed_profile_settings_footer">When work apps are off, they’re paused and can’t be accessed or send you notifications</string>
|
||||
|
||||
<!-- Used as title on the automatic storage manager settings. [CHAR LIMIT=60] -->
|
||||
<string name="automatic_storage_manager_settings">Manage storage</string>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
android:key="audio_routing_ringtone"
|
||||
android:persistent="false"
|
||||
android:title="@string/bluetooth_ringtone_title"
|
||||
settings:controller="com.android.settings.bluetooth.HearingDeviceRingtoneRoutingPreferenceController" />
|
||||
settings:controller="com.android.settings.accessibility.HearingDeviceRingtoneRoutingPreferenceController" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/bluetooth_audio_routing_titles"
|
||||
@@ -40,7 +40,7 @@
|
||||
android:key="audio_routing_call"
|
||||
android:persistent="false"
|
||||
android:title="@string/bluetooth_call_title"
|
||||
settings:controller="com.android.settings.bluetooth.HearingDeviceCallRoutingPreferenceController" />
|
||||
settings:controller="com.android.settings.accessibility.HearingDeviceCallRoutingPreferenceController" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/bluetooth_audio_routing_titles"
|
||||
@@ -49,7 +49,7 @@
|
||||
android:key="audio_routing_media"
|
||||
android:persistent="false"
|
||||
android:title="@string/bluetooth_media_title"
|
||||
settings:controller="com.android.settings.bluetooth.HearingDeviceMediaRoutingPreferenceController" />
|
||||
settings:controller="com.android.settings.accessibility.HearingDeviceMediaRoutingPreferenceController" />
|
||||
|
||||
<ListPreference
|
||||
android:entries="@array/bluetooth_audio_routing_titles"
|
||||
@@ -58,7 +58,7 @@
|
||||
android:key="audio_routing_system_sounds"
|
||||
android:persistent="false"
|
||||
android:title="@string/bluetooth_system_sounds_title"
|
||||
settings:controller="com.android.settings.bluetooth.HearingDeviceSystemSoundsRoutingPreferenceController" />
|
||||
settings:controller="com.android.settings.accessibility.HearingDeviceSystemSoundsRoutingPreferenceController" />
|
||||
|
||||
<com.android.settings.accessibility.AccessibilityFooterPreference
|
||||
android:key="hearing_device_footer"
|
||||
@@ -45,8 +45,17 @@
|
||||
<PreferenceCategory
|
||||
android:key="hearing_options_category"
|
||||
android:title="@string/accessibility_screen_option">
|
||||
|
||||
<Preference
|
||||
android:key="audio_routing"
|
||||
android:title="@string/bluetooth_audio_routing_title"
|
||||
android:summary="@string/bluetooth_audio_routing_summary"
|
||||
android:fragment="com.android.settings.accessibility.AccessibilityAudioRoutingFragment"
|
||||
settings:controller="com.android.settings.accessibility.HearingAidAudioRoutingPreferenceController"/>
|
||||
|
||||
<SwitchPreference
|
||||
android:key="hearing_aid_compatibility"
|
||||
android:order="30"
|
||||
android:title="@string/accessibility_hac_mode_title"
|
||||
android:summary="@string/accessibility_hac_mode_summary"
|
||||
settings:searchable="true"
|
||||
|
||||
@@ -69,12 +69,7 @@
|
||||
android:key="device_companion_apps"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="device_controls_general"
|
||||
android:title="@string/bluetooth_device_controls_general"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="device_controls_specific"
|
||||
android:title="@string/bluetooth_device_controls_specific"/>
|
||||
android:key="device_controls_general" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="spatial_audio_group"/>
|
||||
|
||||
@@ -19,10 +19,9 @@
|
||||
android:key="managed_profile_settings_screen"
|
||||
android:title="@string/managed_profile_settings_title">
|
||||
|
||||
<SwitchPreference
|
||||
<com.android.settingslib.widget.MainSwitchPreference
|
||||
android:key="work_mode"
|
||||
android:title="@string/work_mode_label"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:controller="com.android.settings.accounts.WorkModePreferenceController"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
@@ -38,4 +37,9 @@
|
||||
android:title="@string/cross_profile_calendar_title"
|
||||
settings:controller="com.android.settings.accounts.CrossProfileCalendarPreferenceController"/>
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:title="@string/managed_profile_settings_footer"
|
||||
android:key="managed_profile_footer"
|
||||
settings:searchable="false"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -38,9 +38,10 @@
|
||||
settings:controller="com.android.settings.network.BluetoothWiFiResetPreferenceController" />
|
||||
|
||||
<!-- Reset app preferences -->
|
||||
<Preference
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="reset_app_prefs"
|
||||
android:title="@string/reset_app_preferences" />
|
||||
android:title="@string/reset_app_preferences"
|
||||
settings:userRestriction="no_control_apps" />
|
||||
|
||||
<!-- Erase Euicc data -->
|
||||
<Preference
|
||||
|
||||
@@ -51,8 +51,9 @@ public class SettingsApplication extends Application {
|
||||
// Set Spa environment.
|
||||
setSpaEnvironment();
|
||||
|
||||
if (FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)
|
||||
&& ActivityEmbeddingUtils.isSettingsSplitEnabled(this)) {
|
||||
if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
|
||||
&& FeatureFlagUtils.isEnabled(this,
|
||||
FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)) {
|
||||
if (WizardManagerHelper.isUserSetupComplete(this)) {
|
||||
new ActivityEmbeddingRulesController(this).initRules();
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
|
||||
/** Settings fragment containing bluetooth audio routing. */
|
||||
public class AccessibilityAudioRoutingFragment extends RestrictedDashboardFragment {
|
||||
private static final String TAG = "AccessibilityAudioRoutingFragment";
|
||||
|
||||
public AccessibilityAudioRoutingFragment() {
|
||||
super(DISALLOW_CONFIG_BLUETOOTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.HEARING_AID_AUDIO_ROUTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_audio_routing_fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_audio_routing_fragment);
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHapClient;
|
||||
import android.bluetooth.BluetoothHearingAid;
|
||||
import android.bluetooth.BluetoothLeAudio;
|
||||
@@ -41,20 +40,14 @@ import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settingslib.bluetooth.BluetoothCallback;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.HapClientProfile;
|
||||
import com.android.settingslib.bluetooth.HearingAidInfo;
|
||||
import com.android.settingslib.bluetooth.HearingAidProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Controller that shows and updates the bluetooth device name
|
||||
@@ -73,10 +66,8 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
};
|
||||
|
||||
private final LocalBluetoothManager mLocalBluetoothManager;
|
||||
private final BluetoothAdapter mBluetoothAdapter;
|
||||
private final LocalBluetoothProfileManager mProfileManager;
|
||||
private final CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
|
||||
private final HearingAidHelper mHelper;
|
||||
private FragmentManager mFragmentManager;
|
||||
|
||||
public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
|
||||
@@ -84,8 +75,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager(
|
||||
context);
|
||||
mProfileManager = mLocalBluetoothManager.getProfileManager();
|
||||
mCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
mHelper = new HearingAidHelper(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,7 +86,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return isHearingAidSupported() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return mHelper.isHearingAidSupported() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,7 +101,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
// Can't get connected hearing aids when hearing aids related profiles are not ready. The
|
||||
// profiles will be ready after the services are connected. Needs to add listener and
|
||||
// updates the information when all hearing aids related services are connected.
|
||||
if (isAnyHearingAidRelatedProfilesNotReady()) {
|
||||
if (!mHelper.isAllHearingAidRelatedProfilesReady()) {
|
||||
mProfileManager.addServiceListener(this);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +116,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
||||
final CachedBluetoothDevice device = getConnectedHearingAidDevice();
|
||||
final CachedBluetoothDevice device = mHelper.getConnectedHearingAidDevice();
|
||||
if (FeatureFlagUtils.isEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE)) {
|
||||
launchHearingAidPage();
|
||||
@@ -144,10 +134,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||||
return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary);
|
||||
}
|
||||
final CachedBluetoothDevice device = getConnectedHearingAidDevice();
|
||||
final CachedBluetoothDevice device = mHelper.getConnectedHearingAidDevice();
|
||||
if (device == null) {
|
||||
return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary);
|
||||
}
|
||||
@@ -203,7 +190,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
if (!isAnyHearingAidRelatedProfilesNotReady()) {
|
||||
if (mHelper.isAllHearingAidRelatedProfilesReady()) {
|
||||
updateState(mHearingAidPreference);
|
||||
mProfileManager.removeServiceListener(this);
|
||||
}
|
||||
@@ -218,53 +205,8 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
|
||||
mFragmentManager = fragmentManager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
CachedBluetoothDevice getConnectedHearingAidDevice() {
|
||||
final List<BluetoothDevice> deviceList = getConnectedHearingAidDeviceList();
|
||||
return deviceList.isEmpty() ? null : mCachedDeviceManager.findDevice(deviceList.get(0));
|
||||
}
|
||||
|
||||
private int getConnectedHearingAidDeviceNum() {
|
||||
return getConnectedHearingAidDeviceList().size();
|
||||
}
|
||||
|
||||
private List<BluetoothDevice> getConnectedHearingAidDeviceList() {
|
||||
if (!isHearingAidSupported()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
final List<BluetoothDevice> deviceList = new ArrayList<>();
|
||||
final HapClientProfile hapClientProfile = mProfileManager.getHapClientProfile();
|
||||
if (hapClientProfile != null) {
|
||||
deviceList.addAll(hapClientProfile.getConnectedDevices());
|
||||
}
|
||||
final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
|
||||
if (hearingAidProfile != null) {
|
||||
deviceList.addAll(hearingAidProfile.getConnectedDevices());
|
||||
}
|
||||
return deviceList.stream()
|
||||
.distinct()
|
||||
.filter(d -> !mCachedDeviceManager.isSubDevice(d)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean isHearingAidSupported() {
|
||||
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles();
|
||||
return supportedList.contains(BluetoothProfile.HEARING_AID)
|
||||
|| supportedList.contains(BluetoothProfile.HAP_CLIENT);
|
||||
}
|
||||
|
||||
private boolean isAnyHearingAidRelatedProfilesNotReady() {
|
||||
HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
|
||||
if (hearingAidProfile != null && !hearingAidProfile.isProfileReady()) {
|
||||
return true;
|
||||
}
|
||||
HapClientProfile hapClientProfile = mProfileManager.getHapClientProfile();
|
||||
if (hapClientProfile != null && !hapClientProfile.isProfileReady()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return mHelper.getConnectedHearingAidDeviceList().size();
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
|
||||
@@ -39,7 +39,7 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
|
||||
|
||||
private static final String TAG = "AccessibilityHearingAidsFragment";
|
||||
private static final String KEY_HEARING_OPTIONS_CATEGORY = "hearing_options_category";
|
||||
private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
|
||||
private static final int SHORTCUT_PREFERENCE_IN_CATEGORY_INDEX = 20;
|
||||
private String mFeatureName;
|
||||
|
||||
public AccessibilityHearingAidsFragment() {
|
||||
@@ -65,7 +65,7 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
final PreferenceCategory controlCategory = findPreference(KEY_HEARING_OPTIONS_CATEGORY);
|
||||
// To move the shortcut preference under controlCategory need to remove the original added.
|
||||
mShortcutPreference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
|
||||
mShortcutPreference.setOrder(SHORTCUT_PREFERENCE_IN_CATEGORY_INDEX);
|
||||
getPreferenceScreen().removePreference(mShortcutPreference);
|
||||
controlCategory.addPreference(mShortcutPreference);
|
||||
return view;
|
||||
|
||||
@@ -322,6 +322,18 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
||||
getComponentName());
|
||||
};
|
||||
|
||||
private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
|
||||
int resId;
|
||||
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
|
||||
resId = R.string.accessibility_shortcut_edit_summary_software;
|
||||
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
|
||||
} else {
|
||||
resId = R.string.accessibility_shortcut_edit_summary_software;
|
||||
}
|
||||
return context.getText(resId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be invoked when a button in the tutorial dialog is clicked.
|
||||
*
|
||||
@@ -429,11 +441,9 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
||||
getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE);
|
||||
|
||||
final List<CharSequence> list = new ArrayList<>();
|
||||
final CharSequence softwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_edit_summary_software);
|
||||
|
||||
if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.SOFTWARE)) {
|
||||
list.add(softwareTitle);
|
||||
list.add(getSoftwareShortcutTypeSummary(context));
|
||||
}
|
||||
if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.HARDWARE)) {
|
||||
final CharSequence hardwareTitle = context.getText(
|
||||
@@ -443,7 +453,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
||||
|
||||
// Show software shortcut if first time to use.
|
||||
if (list.isEmpty()) {
|
||||
list.add(softwareTitle);
|
||||
list.add(getSoftwareShortcutTypeSummary(context));
|
||||
}
|
||||
|
||||
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
/**
|
||||
* The controller of the audio routing.
|
||||
*/
|
||||
public class HearingAidAudioRoutingPreferenceController extends BasePreferenceController {
|
||||
public HearingAidAudioRoutingPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)
|
||||
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
}
|
||||
114
src/com/android/settings/accessibility/HearingAidHelper.java
Normal file
114
src/com/android/settings/accessibility/HearingAidHelper.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.HapClientProfile;
|
||||
import com.android.settingslib.bluetooth.HearingAidProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A helper class to get and check hearing aids and its status.
|
||||
*/
|
||||
public class HearingAidHelper {
|
||||
|
||||
private final BluetoothAdapter mBluetoothAdapter;
|
||||
private final LocalBluetoothProfileManager mProfileManager;
|
||||
private final CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
|
||||
public HearingAidHelper(Context context) {
|
||||
final LocalBluetoothManager localBluetoothManager =
|
||||
com.android.settings.bluetooth.Utils.getLocalBluetoothManager(context);
|
||||
mProfileManager = localBluetoothManager.getProfileManager();
|
||||
mCachedDeviceManager = localBluetoothManager.getCachedDeviceManager();
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connected hearing aids device whose profiles are
|
||||
* {@link BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#HAP_CLIENT}.
|
||||
*
|
||||
* @return a list of hearing aids {@link BluetoothDevice} objects
|
||||
*/
|
||||
public List<BluetoothDevice> getConnectedHearingAidDeviceList() {
|
||||
if (!isHearingAidSupported()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
final List<BluetoothDevice> deviceList = new ArrayList<>();
|
||||
final HapClientProfile hapClientProfile = mProfileManager.getHapClientProfile();
|
||||
if (hapClientProfile != null) {
|
||||
deviceList.addAll(hapClientProfile.getConnectedDevices());
|
||||
}
|
||||
final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
|
||||
if (hearingAidProfile != null) {
|
||||
deviceList.addAll(hearingAidProfile.getConnectedDevices());
|
||||
}
|
||||
return deviceList.stream()
|
||||
.distinct()
|
||||
.filter(d -> !mCachedDeviceManager.isSubDevice(d)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first connected hearing aids device.
|
||||
*
|
||||
* @return a {@link CachedBluetoothDevice} that is hearing aids device
|
||||
*/
|
||||
public CachedBluetoothDevice getConnectedHearingAidDevice() {
|
||||
final List<BluetoothDevice> deviceList = getConnectedHearingAidDeviceList();
|
||||
return deviceList.isEmpty() ? null : mCachedDeviceManager.findDevice(deviceList.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#HAP_CLIENT}
|
||||
* supported.
|
||||
*/
|
||||
public boolean isHearingAidSupported() {
|
||||
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles();
|
||||
return supportedList.contains(BluetoothProfile.HEARING_AID)
|
||||
|| supportedList.contains(BluetoothProfile.HAP_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@link BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#HAP_CLIENT}
|
||||
* profiles all ready.
|
||||
*/
|
||||
public boolean isAllHearingAidRelatedProfilesReady() {
|
||||
HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
|
||||
if (hearingAidProfile != null && !hearingAidProfile.isProfileReady()) {
|
||||
return false;
|
||||
}
|
||||
HapClientProfile hapClientProfile = mProfileManager.getHapClientProfile();
|
||||
if (hapClientProfile != null && !hapClientProfile.isProfileReady()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -47,19 +47,24 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
|
||||
private static final String TAG = "HARoutingBasePreferenceController";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final HearingAidAudioRoutingHelper mHelper;
|
||||
private final HearingAidAudioRoutingHelper mAudioRoutingHelper;
|
||||
private final HearingAidHelper mHearingAidHelper;
|
||||
|
||||
public HearingDeviceAudioRoutingBasePreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mHelper = new HearingAidAudioRoutingHelper(context);
|
||||
this(context, preferenceKey,
|
||||
new HearingAidAudioRoutingHelper(context),
|
||||
new HearingAidHelper(context));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public HearingDeviceAudioRoutingBasePreferenceController(Context context,
|
||||
String preferenceKey, HearingAidAudioRoutingHelper helper) {
|
||||
String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper,
|
||||
HearingAidHelper hearingAidHelper) {
|
||||
super(context, preferenceKey);
|
||||
mHelper = helper;
|
||||
|
||||
mAudioRoutingHelper = audioRoutingHelper;
|
||||
mHearingAidHelper = hearingAidHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +86,11 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
|
||||
final Integer routingValue = Ints.tryParse((String) newValue);
|
||||
|
||||
saveRoutingValue(mContext, routingValue);
|
||||
trySetAudioRoutingConfig(getSupportedAttributeList(), getHearingDevice(), routingValue);
|
||||
final CachedBluetoothDevice device = mHearingAidHelper.getConnectedHearingAidDevice();
|
||||
if (device != null) {
|
||||
trySetAudioRoutingConfig(getSupportedAttributeList(),
|
||||
mHearingAidHelper.getConnectedHearingAidDevice(), routingValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -89,10 +98,10 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
|
||||
private void trySetAudioRoutingConfig(int[] audioAttributes,
|
||||
CachedBluetoothDevice hearingDevice,
|
||||
@HearingAidAudioRoutingConstants.RoutingValue int routingValue) {
|
||||
final List<AudioProductStrategy> supportedStrategies = mHelper.getSupportedStrategies(
|
||||
audioAttributes);
|
||||
final List<AudioProductStrategy> supportedStrategies =
|
||||
mAudioRoutingHelper.getSupportedStrategies(audioAttributes);
|
||||
final AudioDeviceAttributes hearingDeviceAttributes =
|
||||
mHelper.getMatchedHearingDeviceAttributes(hearingDevice);
|
||||
mAudioRoutingHelper.getMatchedHearingDeviceAttributes(hearingDevice);
|
||||
if (hearingDeviceAttributes == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
@@ -103,8 +112,8 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean status = mHelper.setPreferredDeviceRoutingStrategies(supportedStrategies,
|
||||
hearingDeviceAttributes, routingValue);
|
||||
final boolean status = mAudioRoutingHelper.setPreferredDeviceRoutingStrategies(
|
||||
supportedStrategies, hearingDeviceAttributes, routingValue);
|
||||
|
||||
if (!status) {
|
||||
final List<String> strategiesName = supportedStrategies.stream()
|
||||
@@ -121,12 +130,6 @@ public abstract class HearingDeviceAudioRoutingBasePreferenceController extends
|
||||
*/
|
||||
protected abstract int[] getSupportedAttributeList();
|
||||
|
||||
/**
|
||||
* Gets the {@link CachedBluetoothDevice} hearing device that is used to configure audio
|
||||
* routing.
|
||||
*/
|
||||
protected abstract CachedBluetoothDevice getHearingDevice();
|
||||
|
||||
/**
|
||||
* Saves the routing value.
|
||||
*
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
@@ -35,15 +35,6 @@ public class HearingDeviceCallRoutingPreferenceController extends
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects in this controller. Need to call this before using the controller.
|
||||
*
|
||||
* @param cachedBluetoothDevice the hearing device to configure audio routing
|
||||
*/
|
||||
public void init(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
mHearingDevice = cachedBluetoothDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Utils.isVoiceCapable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
@@ -54,11 +45,6 @@ public class HearingDeviceCallRoutingPreferenceController extends
|
||||
return HearingAidAudioRoutingConstants.CALL_ROUTING_ATTRIBUTES;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedBluetoothDevice getHearingDevice() {
|
||||
return mHearingDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveRoutingValue(Context context, int routingValue) {
|
||||
Settings.Secure.putInt(context.getContentResolver(),
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
@@ -34,26 +34,12 @@ public class HearingDeviceMediaRoutingPreferenceController extends
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects in this controller. Need to call this before using the controller.
|
||||
*
|
||||
* @param cachedBluetoothDevice the hearing device to configure audio routing
|
||||
*/
|
||||
public void init(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
mHearingDevice = cachedBluetoothDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] getSupportedAttributeList() {
|
||||
return HearingAidAudioRoutingConstants.MEDIA_ROUTING_ATTRIBUTES;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedBluetoothDevice getHearingDevice() {
|
||||
return mHearingDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveRoutingValue(Context context, int routingValue) {
|
||||
Settings.Secure.putInt(context.getContentResolver(),
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
@@ -34,26 +34,12 @@ public class HearingDeviceRingtoneRoutingPreferenceController extends
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects in this controller. Need to call this before using the controller.
|
||||
*
|
||||
* @param cachedBluetoothDevice the hearing device to configure audio routing
|
||||
*/
|
||||
public void init(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
mHearingDevice = cachedBluetoothDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] getSupportedAttributeList() {
|
||||
return HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTE;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedBluetoothDevice getHearingDevice() {
|
||||
return mHearingDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveRoutingValue(Context context, int routingValue) {
|
||||
Settings.Secure.putInt(context.getContentResolver(),
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
@@ -35,26 +35,12 @@ public class HearingDeviceSystemSoundsRoutingPreferenceController extends
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes objects in this controller. Need to call this before using the controller.
|
||||
*
|
||||
* @param cachedBluetoothDevice the hearing device to configure audio routing
|
||||
*/
|
||||
public void init(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
mHearingDevice = cachedBluetoothDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] getSupportedAttributeList() {
|
||||
return HearingAidAudioRoutingConstants.SYSTEM_SOUNDS_ROUTING_ATTRIBUTES;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedBluetoothDevice getHearingDevice() {
|
||||
return mHearingDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveRoutingValue(Context context, int routingValue) {
|
||||
Settings.Secure.putInt(context.getContentResolver(),
|
||||
@@ -249,12 +249,18 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
super.onProcessArguments(arguments);
|
||||
}
|
||||
|
||||
private void addAlwaysOnSetting(PreferenceCategory generalCategory) {
|
||||
if (!DeviceConfig.getBoolean(
|
||||
private boolean isAlwaysOnSettingEnabled() {
|
||||
final boolean defaultValue = getContext().getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_magnification_always_on_enabled);
|
||||
|
||||
return DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
||||
"AlwaysOnMagnifier__enable_always_on_magnifier",
|
||||
false
|
||||
)) {
|
||||
defaultValue
|
||||
);
|
||||
}
|
||||
private void addAlwaysOnSetting(PreferenceCategory generalCategory) {
|
||||
if (!isAlwaysOnSettingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,37 +20,38 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
import com.android.settings.slices.SliceData;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
public class ContactSearchPreferenceController extends BasePreferenceController implements
|
||||
Preference.OnPreferenceChangeListener {
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
private UserHandle mManagedUser;
|
||||
public class ContactSearchPreferenceController extends TogglePreferenceController implements
|
||||
Preference.OnPreferenceChangeListener, DefaultLifecycleObserver,
|
||||
ManagedProfileQuietModeEnabler.QuietModeChangeListener {
|
||||
|
||||
private final ManagedProfileQuietModeEnabler mQuietModeEnabler;
|
||||
private final UserHandle mManagedUser;
|
||||
private Preference mPreference;
|
||||
|
||||
public ContactSearchPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
// Set default managed profile for the current user, otherwise isAvailable will be false and
|
||||
// the setting won't be searchable.
|
||||
UserManager userManager = context.getSystemService(UserManager.class);
|
||||
mManagedUser = Utils.getManagedProfile(userManager);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setManagedUser(UserHandle managedUser) {
|
||||
mManagedUser = managedUser;
|
||||
mManagedUser = Utils.getManagedProfile(context.getSystemService(UserManager.class));
|
||||
mQuietModeEnabler = new ManagedProfileQuietModeEnabler(context, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return (mManagedUser != null) ? AVAILABLE : DISABLED_FOR_USER;
|
||||
return mQuietModeEnabler.isAvailable() ? AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,6 +60,7 @@ public class ContactSearchPreferenceController extends BasePreferenceController
|
||||
if (preference instanceof RestrictedSwitchPreference) {
|
||||
final RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
|
||||
pref.setChecked(isChecked());
|
||||
pref.setEnabled(!mQuietModeEnabler.isQuietModeEnabled());
|
||||
if (mManagedUser != null) {
|
||||
final RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
|
||||
RestrictedLockUtilsInternal.checkIfRemoteContactSearchDisallowed(
|
||||
@@ -68,26 +70,48 @@ public class ContactSearchPreferenceController extends BasePreferenceController
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isChecked() {
|
||||
if (mManagedUser == null) {
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
updateState(mPreference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NotNull LifecycleOwner lifecycleOwner) {
|
||||
lifecycleOwner.getLifecycle().addObserver(mQuietModeEnabler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NotNull LifecycleOwner lifecycleOwner) {
|
||||
lifecycleOwner.getLifecycle().removeObserver(mQuietModeEnabler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
if (mManagedUser == null || mQuietModeEnabler.isQuietModeEnabled()) {
|
||||
return false;
|
||||
}
|
||||
return 0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier());
|
||||
}
|
||||
|
||||
private boolean setChecked(boolean isChecked) {
|
||||
if (mManagedUser != null) {
|
||||
final int value = isChecked ? 1 : 0;
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, value, mManagedUser.getIdentifier());
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
if (mManagedUser == null || mQuietModeEnabler.isQuietModeEnabled()) {
|
||||
return false;
|
||||
}
|
||||
final int value = isChecked ? 1 : 0;
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, value, mManagedUser.getIdentifier());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
return setChecked((boolean) newValue);
|
||||
public void onQuietModeChanged() {
|
||||
if (mPreference != null) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,4 +119,9 @@ public class ContactSearchPreferenceController extends BasePreferenceController
|
||||
public int getSliceType() {
|
||||
return SliceData.SliceType.SWITCH;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return R.string.menu_key_accounts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.accounts;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A class that controls the managed profile's quiet mode, listens to quiet mode changes and
|
||||
* modifies managed profile settings. The user facing term for quiet mode is "work apps".
|
||||
*/
|
||||
final class ManagedProfileQuietModeEnabler implements DefaultLifecycleObserver {
|
||||
|
||||
private static final String TAG = "QuietModeEnabler";
|
||||
private final Context mContext;
|
||||
private final QuietModeChangeListener mListener;
|
||||
@Nullable private final UserHandle mManagedProfile;
|
||||
private final UserManager mUserManager;
|
||||
|
||||
public interface QuietModeChangeListener {
|
||||
/** Called when quiet mode has changed. */
|
||||
void onQuietModeChanged();
|
||||
}
|
||||
|
||||
ManagedProfileQuietModeEnabler(Context context, QuietModeChangeListener listener) {
|
||||
mContext = context;
|
||||
mListener = listener;
|
||||
mUserManager = context.getSystemService(UserManager.class);
|
||||
mManagedProfile = Utils.getManagedProfile(mUserManager);
|
||||
}
|
||||
|
||||
public void setQuietModeEnabled(boolean enabled) {
|
||||
if (mManagedProfile != null) {
|
||||
mUserManager.requestQuietModeEnabled(enabled, mManagedProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isQuietModeEnabled() {
|
||||
return mManagedProfile != null && mUserManager.isQuietModeEnabled(mManagedProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
|
||||
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
|
||||
mContext.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return (mManagedProfile != null);
|
||||
}
|
||||
|
||||
private void refreshQuietMode() {
|
||||
if (mListener != null) {
|
||||
mListener.onQuietModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver that listens to {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and
|
||||
* {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE}, and updates the work mode
|
||||
*/
|
||||
@VisibleForTesting
|
||||
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null) {
|
||||
return;
|
||||
}
|
||||
String action = intent.getAction();
|
||||
Log.v(TAG, "Received broadcast: " + action);
|
||||
|
||||
if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
|
||||
|| Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
|
||||
int intentUserIdentifier = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
|
||||
UserHandle.USER_NULL);
|
||||
if (intentUserIdentifier == mManagedProfile.getIdentifier()) {
|
||||
refreshQuietMode();
|
||||
} else {
|
||||
Log.w(TAG, "Managed profile broadcast ID: " + intentUserIdentifier
|
||||
+ " does not match managed user: " + mManagedProfile);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,165 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of the License at
|
||||
* 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.
|
||||
* 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.accounts;
|
||||
|
||||
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_OFF_SUMMARY;
|
||||
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_ON_SUMMARY;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.slices.SliceData;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreferenceController;
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
public class WorkModePreferenceController extends BasePreferenceController implements
|
||||
Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop {
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
private static final String TAG = "WorkModeController";
|
||||
|
||||
private UserManager mUserManager;
|
||||
private UserHandle mManagedUser;
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
public class WorkModePreferenceController extends SettingsMainSwitchPreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, DefaultLifecycleObserver,
|
||||
ManagedProfileQuietModeEnabler.QuietModeChangeListener {
|
||||
|
||||
private Preference mPreference;
|
||||
private IntentFilter mIntentFilter;
|
||||
private final ManagedProfileQuietModeEnabler mQuietModeEnabler;
|
||||
|
||||
public WorkModePreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
|
||||
mIntentFilter = new IntentFilter();
|
||||
mIntentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
|
||||
mIntentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
|
||||
// Set default managed profile for the current user, otherwise isAvailable will be false and
|
||||
// the setting won't be searchable.
|
||||
mManagedUser = Utils.getManagedProfile(mUserManager);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setManagedUser(UserHandle managedUser) {
|
||||
mManagedUser = managedUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mContext.registerReceiver(mReceiver, mIntentFilter,
|
||||
Context.RECEIVER_EXPORTED_UNAUDITED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mQuietModeEnabler = new ManagedProfileQuietModeEnabler(context, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return (mManagedUser != null) ? AVAILABLE : DISABLED_FOR_USER;
|
||||
return (mQuietModeEnabler.isAvailable()) ? AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
public void onStart(@NotNull LifecycleOwner lifecycleOwner) {
|
||||
lifecycleOwner.getLifecycle().addObserver(mQuietModeEnabler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (isChecked()) {
|
||||
return mDevicePolicyManager.getResources().getString(
|
||||
WORK_PROFILE_SETTING_ON_SUMMARY,
|
||||
() -> mContext.getString(R.string.work_mode_on_summary));
|
||||
}
|
||||
|
||||
return mDevicePolicyManager.getResources().getString(
|
||||
WORK_PROFILE_SETTING_OFF_SUMMARY,
|
||||
() -> mContext.getString(R.string.work_mode_off_summary));
|
||||
public void onStop(@NotNull LifecycleOwner lifecycleOwner) {
|
||||
lifecycleOwner.getLifecycle().removeObserver(mQuietModeEnabler);
|
||||
}
|
||||
|
||||
private boolean isChecked() {
|
||||
boolean isWorkModeOn = false;
|
||||
if (mUserManager != null && mManagedUser != null) {
|
||||
isWorkModeOn = !mUserManager.isQuietModeEnabled(mManagedUser);
|
||||
}
|
||||
return isWorkModeOn;
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return !mQuietModeEnabler.isQuietModeEnabled();
|
||||
}
|
||||
|
||||
private boolean setChecked(boolean isChecked) {
|
||||
if (mUserManager != null && mManagedUser != null) {
|
||||
final boolean quietModeEnabled = !isChecked;
|
||||
mUserManager.requestQuietModeEnabled(quietModeEnabled, mManagedUser);
|
||||
}
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
mQuietModeEnabler.setQuietModeEnabled(!isChecked);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
if (preference instanceof TwoStatePreference) {
|
||||
((TwoStatePreference) preference).setChecked(isChecked());
|
||||
}
|
||||
public void onQuietModeChanged() {
|
||||
updateState(mSwitchPreference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
return setChecked((boolean) newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver that listens to {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and
|
||||
* {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE}, and updates the work mode
|
||||
*/
|
||||
@VisibleForTesting
|
||||
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null) {
|
||||
return;
|
||||
}
|
||||
final String action = intent.getAction();
|
||||
Log.v(TAG, "Received broadcast: " + action);
|
||||
|
||||
if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
|
||||
|| Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
|
||||
if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
|
||||
UserHandle.USER_NULL) == mManagedUser.getIdentifier()) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@SliceData.SliceType
|
||||
public int getSliceType() {
|
||||
return SliceData.SliceType.SWITCH;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return R.string.menu_key_accounts;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setPreference(MainSwitchPreference preference) {
|
||||
mSwitchPreference = preference;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,16 +97,24 @@ public class ActivityEmbeddingUtils {
|
||||
* </ul>
|
||||
*/
|
||||
public static boolean isEmbeddingActivityEnabled(Context context) {
|
||||
boolean isFlagEnabled = FeatureFlagUtils.isEnabled(context,
|
||||
FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN);
|
||||
boolean isSettingsSplitSupported = isSettingsSplitEnabled(context);
|
||||
boolean isUserSetupComplete = WizardManagerHelper.isUserSetupComplete(context);
|
||||
|
||||
Log.d(TAG, "isFlagEnabled = " + isFlagEnabled);
|
||||
Log.d(TAG, "isSettingsSplitSupported = " + isSettingsSplitSupported);
|
||||
Log.d(TAG, "isUserSetupComplete = " + isUserSetupComplete);
|
||||
|
||||
return isFlagEnabled && isSettingsSplitSupported && isUserSetupComplete;
|
||||
// Activity Embedding feature is not enabled if Settings doesn't enable large screen
|
||||
// optimization or the device is not supported.
|
||||
if (!isSettingsSplitEnabled(context)) {
|
||||
Log.d(TAG, "isSettingsSplitSupported = false");
|
||||
return false;
|
||||
}
|
||||
// Activity Embedding feature is not enabled if a user chooses to disable the feature.
|
||||
if (!FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)) {
|
||||
Log.d(TAG, "isFlagEnabled = false");
|
||||
return false;
|
||||
}
|
||||
// Don't enable Activity embedding for setup wizard.
|
||||
if (!WizardManagerHelper.isUserSetupComplete(context)) {
|
||||
Log.d(TAG, "isUserSetupComplete = false");
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, "isEmbeddingActivityEnabled = true");
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Whether to show the regular or simplified homepage layout. */
|
||||
@@ -120,8 +128,7 @@ public class ActivityEmbeddingUtils {
|
||||
* Check if activity is already embedded
|
||||
*/
|
||||
public static boolean isAlreadyEmbedded(Activity activity) {
|
||||
return ActivityEmbeddingController
|
||||
.getInstance(activity)
|
||||
.isActivityEmbedded(activity);
|
||||
return isEmbeddingActivityEnabled(activity) && ActivityEmbeddingController.getInstance(
|
||||
activity).isActivityEmbedded(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class CombinedProviderInfo {
|
||||
@Nullable List<CredentialProviderInfo> cpis,
|
||||
@Nullable AutofillServiceInfo asi,
|
||||
boolean isDefaultAutofillProvider,
|
||||
boolean IsPrimaryCredmanProvider) {
|
||||
boolean isPrimaryCredmanProvider) {
|
||||
if (cpis == null) {
|
||||
mCredentialProviderInfos = new ArrayList<>();
|
||||
} else {
|
||||
@@ -59,11 +59,11 @@ public final class CombinedProviderInfo {
|
||||
}
|
||||
mAutofillServiceInfo = asi;
|
||||
mIsDefaultAutofillProvider = isDefaultAutofillProvider;
|
||||
mIsPrimaryCredmanProvider = IsPrimaryCredmanProvider;
|
||||
mIsPrimaryCredmanProvider = isPrimaryCredmanProvider;
|
||||
}
|
||||
|
||||
/** Returns the credential provider info. */
|
||||
@Nullable
|
||||
@NonNull
|
||||
public List<CredentialProviderInfo> getCredentialProviderInfos() {
|
||||
return mCredentialProviderInfos;
|
||||
}
|
||||
@@ -85,11 +85,13 @@ public final class CombinedProviderInfo {
|
||||
/** Returns the app icon. */
|
||||
@Nullable
|
||||
public Drawable getAppIcon(@NonNull Context context, int userId) {
|
||||
IconDrawableFactory factory = IconDrawableFactory.newInstance(context);
|
||||
final IconDrawableFactory factory = IconDrawableFactory.newInstance(context);
|
||||
final ServiceInfo brandingService = getBrandingService();
|
||||
final ApplicationInfo appInfo = getApplicationInfo();
|
||||
|
||||
Drawable icon = null;
|
||||
ServiceInfo brandingService = getBrandingService();
|
||||
if (brandingService != null) {
|
||||
icon = factory.getBadgedIcon(brandingService, getApplicationInfo(), userId);
|
||||
if (brandingService != null && appInfo != null) {
|
||||
icon = factory.getBadgedIcon(brandingService, appInfo, userId);
|
||||
}
|
||||
|
||||
// If the branding service gave us a icon then use that.
|
||||
@@ -98,7 +100,10 @@ public final class CombinedProviderInfo {
|
||||
}
|
||||
|
||||
// Otherwise fallback to the app icon and then the package name.
|
||||
return factory.getBadgedIcon(getApplicationInfo(), userId);
|
||||
if (appInfo != null) {
|
||||
return factory.getBadgedIcon(appInfo, userId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the app name. */
|
||||
@@ -116,11 +121,14 @@ public final class CombinedProviderInfo {
|
||||
}
|
||||
|
||||
// Otherwise fallback to the app label and then the package name.
|
||||
name = getApplicationInfo().loadLabel(context.getPackageManager());
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
name = getApplicationInfo().packageName;
|
||||
final ApplicationInfo appInfo = getApplicationInfo();
|
||||
if (appInfo != null) {
|
||||
name = appInfo.loadLabel(context.getPackageManager());
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
return appInfo.packageName;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Gets the service to use for branding (name, icons). */
|
||||
@@ -245,8 +253,10 @@ public final class CombinedProviderInfo {
|
||||
// Now go through and build the joint datasets.
|
||||
List<CombinedProviderInfo> cmpi = new ArrayList<>();
|
||||
for (String packageName : packageNames) {
|
||||
List<AutofillServiceInfo> asi = autofillServices.get(packageName);
|
||||
List<CredentialProviderInfo> cpi = credmanServices.get(packageName);
|
||||
List<AutofillServiceInfo> asi =
|
||||
autofillServices.getOrDefault(packageName, new ArrayList<>());
|
||||
List<CredentialProviderInfo> cpi =
|
||||
credmanServices.getOrDefault(packageName, new ArrayList<>());
|
||||
|
||||
// If there are multiple autofill services then pick the first one.
|
||||
AutofillServiceInfo selectedAsi = null;
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.view.autofill.AutofillManager;
|
||||
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
|
||||
import com.android.settingslib.applications.DefaultAppInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController {
|
||||
@@ -110,15 +111,19 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
private List<CombinedProviderInfo> getAllProviders(int userId) {
|
||||
final List<AutofillServiceInfo> autofillProviders =
|
||||
AutofillServiceInfo.getAvailableServices(mContext, userId);
|
||||
final List<CredentialProviderInfo> credManProviders =
|
||||
mCredentialManager.getCredentialProviderServices(
|
||||
userId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
|
||||
final String selectedAutofillProvider =
|
||||
Settings.Secure.getStringForUser(
|
||||
mContext.getContentResolver(),
|
||||
DefaultCombinedPicker.AUTOFILL_SETTING,
|
||||
userId);
|
||||
|
||||
final List<CredentialProviderInfo> credManProviders = new ArrayList<>();
|
||||
if (mCredentialManager != null) {
|
||||
credManProviders.addAll(
|
||||
mCredentialManager.getCredentialProviderServices(
|
||||
userId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY));
|
||||
}
|
||||
|
||||
return CombinedProviderInfo.buildMergedList(
|
||||
autofillProviders, credManProviders, selectedAutofillProvider);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.settings.applications.manageapplications;
|
||||
|
||||
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_SPA;
|
||||
|
||||
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
|
||||
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||
|
||||
@@ -67,7 +65,6 @@ import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -728,9 +725,6 @@ public class ManageApplications extends InstrumentedFragment
|
||||
R.string.long_background_tasks_label);
|
||||
break;
|
||||
case LIST_TYPE_CLONED_APPS:
|
||||
if (!FeatureFlagUtils.isEnabled(getContext(), SETTINGS_ENABLE_SPA)) {
|
||||
return;
|
||||
}
|
||||
int userId = UserHandle.getUserId(mCurrentUid);
|
||||
UserInfo userInfo = mUserManager.getUserInfo(userId);
|
||||
if (userInfo != null && !userInfo.isCloneProfile()) {
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.bluetooth;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
/**
|
||||
* The controller of the audio routing in the bluetooth detail settings.
|
||||
*/
|
||||
public class BluetoothDetailsAudioRoutingController extends BluetoothDetailsController {
|
||||
|
||||
private static final String KEY_DEVICE_CONTROLS_SPECIFIC_GROUP = "device_controls_specific";
|
||||
@VisibleForTesting
|
||||
static final String KEY_AUDIO_ROUTING = "audio_routing";
|
||||
|
||||
public BluetoothDetailsAudioRoutingController(Context context,
|
||||
PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
|
||||
super(context, fragment, device, lifecycle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mCachedDevice.isHearingAidDevice() && FeatureFlagUtils.isEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_AUDIO_ROUTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(PreferenceScreen screen) {
|
||||
if (!mCachedDevice.isHearingAidDevice()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PreferenceCategory prefCategory = screen.findPreference(getPreferenceKey());
|
||||
final Preference pref = createAudioRoutingPreference(prefCategory.getContext());
|
||||
prefCategory.addPreference(pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refresh() {}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY_DEVICE_CONTROLS_SPECIFIC_GROUP;
|
||||
}
|
||||
|
||||
private Preference createAudioRoutingPreference(Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
|
||||
preference.setKey(KEY_AUDIO_ROUTING);
|
||||
preference.setTitle(context.getString(R.string.bluetooth_audio_routing_title));
|
||||
preference.setSummary(context.getString(R.string.bluetooth_audio_routing_summary));
|
||||
final Bundle extras = preference.getExtras();
|
||||
extras.putString(KEY_DEVICE_ADDRESS, mCachedDevice.getAddress());
|
||||
preference.setFragment(BluetoothDetailsAudioRoutingFragment.class.getName());
|
||||
|
||||
return preference;
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* 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.bluetooth;
|
||||
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
/** Settings fragment containing bluetooth audio routing. */
|
||||
public class BluetoothDetailsAudioRoutingFragment extends RestrictedDashboardFragment {
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.bluetooth_audio_routing_fragment);
|
||||
private static final String TAG = "BluetoothDetailsAudioRoutingFragment";
|
||||
@VisibleForTesting
|
||||
CachedBluetoothDevice mCachedDevice;
|
||||
|
||||
public BluetoothDetailsAudioRoutingFragment() {
|
||||
super(DISALLOW_CONFIG_BLUETOOTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
final LocalBluetoothManager localBtMgr = Utils.getLocalBtManager(context);
|
||||
final CachedBluetoothDeviceManager cachedDeviceMgr = localBtMgr.getCachedDeviceManager();
|
||||
final BluetoothDevice bluetoothDevice = localBtMgr.getBluetoothAdapter().getRemoteDevice(
|
||||
getArguments().getString(KEY_DEVICE_ADDRESS));
|
||||
|
||||
mCachedDevice = cachedDeviceMgr.findDevice(bluetoothDevice);
|
||||
if (mCachedDevice == null) {
|
||||
// Close this page if device is null with invalid device mac address
|
||||
Log.w(TAG, "onAttach() CachedDevice is null! Can not find address: "
|
||||
+ bluetoothDevice.getAnonymizedAddress());
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
use(HearingDeviceRingtoneRoutingPreferenceController.class).init(mCachedDevice);
|
||||
use(HearingDeviceCallRoutingPreferenceController.class).init(mCachedDevice);
|
||||
use(HearingDeviceMediaRoutingPreferenceController.class).init(mCachedDevice);
|
||||
use(HearingDeviceSystemSoundsRoutingPreferenceController.class).init(mCachedDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.BLUETOOTH_AUDIO_ROUTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.bluetooth_audio_routing_fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* The controller of the hearing device controls in the bluetooth detail settings.
|
||||
* The controller of the hearing device settings to launch Hearing device page.
|
||||
*/
|
||||
public class BluetoothDetailsHearingDeviceControlsController extends BluetoothDetailsController
|
||||
implements Preference.OnPreferenceClickListener {
|
||||
@@ -87,6 +87,7 @@ public class BluetoothDetailsHearingDeviceControlsController extends BluetoothDe
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setKey(KEY_HEARING_DEVICE_CONTROLS);
|
||||
preference.setTitle(context.getString(R.string.bluetooth_device_controls_title));
|
||||
preference.setSummary(context.getString(R.string.bluetooth_device_controls_summary));
|
||||
preference.setOnPreferenceClickListener(this);
|
||||
|
||||
return preference;
|
||||
|
||||
@@ -314,8 +314,6 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
lifecycle));
|
||||
controllers.add(new BluetoothDetailsHearingDeviceControlsController(context, this,
|
||||
mCachedDevice, lifecycle));
|
||||
controllers.add(new BluetoothDetailsAudioRoutingController(context, this, mCachedDevice,
|
||||
lifecycle));
|
||||
}
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,10 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -125,7 +127,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
|
||||
update(cachedBluetoothDevice);
|
||||
}
|
||||
} else {
|
||||
removeAllDevicesFromPreference();
|
||||
removeAllDevicesFromPreference();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +254,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
|
||||
btPreference.setOnGearClickListener(mDeviceProfilesListener);
|
||||
if (this instanceof Preference.OnPreferenceClickListener) {
|
||||
btPreference.setOnPreferenceClickListener(
|
||||
(Preference.OnPreferenceClickListener)this);
|
||||
(Preference.OnPreferenceClickListener) this);
|
||||
}
|
||||
mPreferenceMap.put(device, btPreference);
|
||||
mDevicePreferenceCallback.onDeviceAdded(btPreference);
|
||||
@@ -266,17 +268,20 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
|
||||
final BluetoothDevice device = cachedDevice.getDevice();
|
||||
final CachedBluetoothDevice subCachedDevice = cachedDevice.getSubDevice();
|
||||
if (mPreferenceMap.containsKey(device)) {
|
||||
mDevicePreferenceCallback.onDeviceRemoved(mPreferenceMap.get(device));
|
||||
mPreferenceMap.remove(device);
|
||||
removePreference(device);
|
||||
} else if (subCachedDevice != null) {
|
||||
// When doing remove, to check if preference maps to sub device.
|
||||
// This would happen when connection state is changed in detail page that there is no
|
||||
// callback from SettingsLib.
|
||||
final BluetoothDevice subDevice = subCachedDevice.getDevice();
|
||||
if (mPreferenceMap.containsKey(subDevice)) {
|
||||
mDevicePreferenceCallback.onDeviceRemoved(mPreferenceMap.get(subDevice));
|
||||
mPreferenceMap.remove(subDevice);
|
||||
}
|
||||
removePreference(subDevice);
|
||||
}
|
||||
}
|
||||
|
||||
private void removePreference(BluetoothDevice device) {
|
||||
if (mPreferenceMap.containsKey(device)) {
|
||||
mDevicePreferenceCallback.onDeviceRemoved(mPreferenceMap.get(device));
|
||||
mPreferenceMap.remove(device);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +329,38 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
|
||||
* Update the attributes of {@link Preference}.
|
||||
*/
|
||||
public void refreshPreference() {
|
||||
for (Preference preference : mPreferenceMap.values()) {
|
||||
((BluetoothDevicePreference) preference).onPreferenceAttributesChanged();
|
||||
List<BluetoothDevice> removeList = new ArrayList<>();
|
||||
mPreferenceMap.forEach((key, preference) -> {
|
||||
if (isDeviceOfMapInCachedDevicesList(key)) {
|
||||
((BluetoothDevicePreference) preference).onPreferenceAttributesChanged();
|
||||
} else {
|
||||
// If the BluetoothDevice of preference is not in the CachedDevices List, then
|
||||
// remove this preference.
|
||||
removeList.add(key);
|
||||
}
|
||||
});
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : removeList) {
|
||||
Log.d(getLogTag(), "removePreference key: " + bluetoothDevice.getAnonymizedAddress());
|
||||
removePreference(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isDeviceInCachedDevicesList(CachedBluetoothDevice cachedDevice){
|
||||
protected boolean isDeviceInCachedDevicesList(CachedBluetoothDevice cachedDevice) {
|
||||
return mLocalManager.getCachedDeviceManager().getCachedDevicesCopy().contains(cachedDevice);
|
||||
}
|
||||
|
||||
private boolean isDeviceOfMapInCachedDevicesList(BluetoothDevice inputBluetoothDevice) {
|
||||
Collection<CachedBluetoothDevice> cachedDevices =
|
||||
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
|
||||
if (cachedDevices == null || cachedDevices.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return cachedDevices.stream()
|
||||
.anyMatch(cachedBluetoothDevice -> cachedBluetoothDevice.getDevice() != null
|
||||
&& cachedBluetoothDevice.getDevice().equals(inputBluetoothDevice));
|
||||
}
|
||||
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWIT
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -75,6 +76,8 @@ import com.android.settingslib.drawer.TileUtils;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.settingslib.widget.AdaptiveIcon;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -152,7 +155,14 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
||||
}
|
||||
bindIcon(pref, tile, forceRoundedIcon);
|
||||
|
||||
if (tile instanceof ActivityTile) {
|
||||
if (tile.hasPendingIntent()) {
|
||||
// Pending intent cannot be launched within the settings app panel, and will thus always
|
||||
// be executed directly.
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
launchPendingIntentOrSelectProfile(activity, tile, fragment.getMetricsCategory());
|
||||
return true;
|
||||
});
|
||||
} else if (tile instanceof ActivityTile) {
|
||||
final int sourceMetricsCategory = fragment.getMetricsCategory();
|
||||
final Bundle metadata = tile.getMetaData();
|
||||
String clsName = null;
|
||||
@@ -441,6 +451,33 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
||||
preference.setIcon(iconDrawable);
|
||||
}
|
||||
|
||||
private void launchPendingIntentOrSelectProfile(FragmentActivity activity, Tile tile,
|
||||
int sourceMetricCategory) {
|
||||
ProfileSelectDialog.updatePendingIntentsIfNeeded(mContext, tile);
|
||||
|
||||
if (tile.pendingIntentMap.isEmpty()) {
|
||||
Log.w(TAG, "Cannot resolve pendingIntent, skipping. " + tile.getIntent());
|
||||
return;
|
||||
}
|
||||
|
||||
mMetricsFeatureProvider.logSettingsTileClick(tile.getKey(mContext), sourceMetricCategory);
|
||||
|
||||
// Launch the pending intent directly if there's only one available.
|
||||
if (tile.pendingIntentMap.size() == 1) {
|
||||
PendingIntent pendingIntent = Iterables.getOnlyElement(tile.pendingIntentMap.values());
|
||||
try {
|
||||
pendingIntent.send();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.w(TAG, "Failed executing pendingIntent. " + pendingIntent.getIntent(), e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile,
|
||||
sourceMetricCategory, /* onShowListener= */ null,
|
||||
/* onDismissListener= */ null, /* onCancelListener= */ null);
|
||||
}
|
||||
|
||||
private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent,
|
||||
int sourceMetricCategory, TopLevelHighlightMixin highlightMixin,
|
||||
boolean isDuplicateClick) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
@@ -47,7 +48,6 @@ import com.android.settingslib.PrimarySwitchPreference;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.ProviderTile;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
import com.android.settingslib.search.Indexable;
|
||||
|
||||
@@ -55,6 +55,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -504,6 +505,10 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
||||
// Install dashboard tiles and collect pending observers.
|
||||
final boolean forceRoundedIcons = shouldForceRoundedIcon();
|
||||
final List<DynamicDataObserver> pendingObservers = new ArrayList<>();
|
||||
|
||||
// Move group tiles to the beginning of the list to ensure they are created before the
|
||||
// other tiles.
|
||||
tiles.sort(Comparator.comparingInt(tile -> tile.getType() == Tile.Type.GROUP ? 0 : 1));
|
||||
for (Tile tile : tiles) {
|
||||
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
@@ -526,7 +531,14 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
||||
observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
|
||||
getActivity(), this, forceRoundedIcons, pref, tile, key,
|
||||
mPlaceholderPreferenceController.getOrder());
|
||||
screen.addPreference(pref);
|
||||
if (tile.hasGroupKey() && mDashboardTilePrefKeys.containsKey(tile.getGroupKey())) {
|
||||
final Preference group = screen.findPreference(tile.getGroupKey());
|
||||
if (group instanceof PreferenceCategory) {
|
||||
((PreferenceCategory) group).addPreference(pref);
|
||||
}
|
||||
} else {
|
||||
screen.addPreference(pref);
|
||||
}
|
||||
registerDynamicDataObservers(observers);
|
||||
mDashboardTilePrefKeys.put(key, observers);
|
||||
}
|
||||
@@ -569,11 +581,28 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
||||
}
|
||||
|
||||
protected Preference createPreference(Tile tile) {
|
||||
return tile instanceof ProviderTile
|
||||
? new SwitchPreference(getPrefContext())
|
||||
: tile.hasSwitch()
|
||||
? new PrimarySwitchPreference(getPrefContext())
|
||||
: new Preference(getPrefContext());
|
||||
switch (tile.getType()) {
|
||||
case EXTERNAL_ACTION:
|
||||
Preference externalActionPreference = new Preference(getPrefContext());
|
||||
externalActionPreference
|
||||
.setWidgetLayoutResource(R.layout.preference_external_action_icon);
|
||||
return externalActionPreference;
|
||||
case SWITCH:
|
||||
return new SwitchPreference(getPrefContext());
|
||||
case SWITCH_WITH_ACTION:
|
||||
return new PrimarySwitchPreference(getPrefContext());
|
||||
case GROUP:
|
||||
mMetricsFeatureProvider.action(
|
||||
mMetricsFeatureProvider.getAttribution(getActivity()),
|
||||
SettingsEnums.ACTION_SETTINGS_GROUP_TILE_ADDED_TO_SCREEN,
|
||||
getMetricsCategory(),
|
||||
tile.getKey(getContext()),
|
||||
/* value= */ 0);
|
||||
return new PreferenceCategory((getPrefContext()));
|
||||
case ACTION:
|
||||
default:
|
||||
return new Preference(getPrefContext());
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.dashboard.profileselector;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
@@ -127,13 +128,25 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O
|
||||
@Override
|
||||
public void onClick(int position) {
|
||||
final UserHandle user = mSelectedTile.userHandle.get(position);
|
||||
// Show menu on top level items.
|
||||
final Intent intent = new Intent(mSelectedTile.getIntent());
|
||||
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()
|
||||
.logStartedIntentWithProfile(intent, mSourceMetricCategory,
|
||||
position == 1 /* isWorkProfile */);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
getActivity().startActivityAsUser(intent, user);
|
||||
if (!mSelectedTile.hasPendingIntent()) {
|
||||
final Intent intent = new Intent(mSelectedTile.getIntent());
|
||||
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()
|
||||
.logStartedIntentWithProfile(intent, mSourceMetricCategory,
|
||||
position == 1 /* isWorkProfile */);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
getActivity().startActivityAsUser(intent, user);
|
||||
} else {
|
||||
PendingIntent pendingIntent = mSelectedTile.pendingIntentMap.get(user);
|
||||
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()
|
||||
.logSettingsTileClickWithProfile(mSelectedTile.getKey(getContext()),
|
||||
mSourceMetricCategory,
|
||||
position == 1 /* isWorkProfile */);
|
||||
try {
|
||||
pendingIntent.send();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.w(TAG, "Failed executing pendingIntent. " + pendingIntent.getIntent(), e);
|
||||
}
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -178,4 +191,36 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the userHandle and pendingIntentMap in the provided tile, and remove the invalid
|
||||
* entries if any.
|
||||
*/
|
||||
public static void updatePendingIntentsIfNeeded(Context context, Tile tile) {
|
||||
if (tile.userHandle == null || tile.userHandle.size() <= 1
|
||||
|| tile.pendingIntentMap.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
for (UserHandle userHandle : List.copyOf(tile.userHandle)) {
|
||||
if (!tile.pendingIntentMap.containsKey(userHandle)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Delete the user without pending intent: "
|
||||
+ userHandle.getIdentifier());
|
||||
}
|
||||
tile.userHandle.remove(userHandle);
|
||||
}
|
||||
}
|
||||
|
||||
final UserManager userManager = UserManager.get(context);
|
||||
for (UserHandle userHandle : List.copyOf(tile.pendingIntentMap.keySet())) {
|
||||
UserInfo userInfo = userManager.getUserInfo(userHandle.getIdentifier());
|
||||
if (userInfo == null || userInfo.isCloneProfile()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Delete the user: " + userHandle.getIdentifier());
|
||||
}
|
||||
tile.userHandle.remove(userHandle);
|
||||
tile.pendingIntentMap.remove(userHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,18 +322,11 @@ public class BatteryInfo {
|
||||
final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());
|
||||
if (drainTimeUs > 0) {
|
||||
info.remainingTimeUs = drainTimeUs;
|
||||
info.remainingLabel = PowerUtil.getBatteryRemainingStringFormatted(
|
||||
info.remainingLabel = PowerUtil.getBatteryRemainingShortStringFormatted(
|
||||
context,
|
||||
PowerUtil.convertUsToMs(drainTimeUs),
|
||||
null /* percentageString */,
|
||||
false /* basedOnUsage */
|
||||
);
|
||||
info.chargeLabel = PowerUtil.getBatteryRemainingStringFormatted(
|
||||
context,
|
||||
PowerUtil.convertUsToMs(drainTimeUs),
|
||||
info.batteryPercentString,
|
||||
estimate.isBasedOnUsage() && !shortString
|
||||
PowerUtil.convertUsToMs(drainTimeUs)
|
||||
);
|
||||
info.chargeLabel = info.remainingLabel;
|
||||
info.suggestionLabel = PowerUtil.getBatteryTipStringFormatted(
|
||||
context, PowerUtil.convertUsToMs(drainTimeUs));
|
||||
} else {
|
||||
|
||||
@@ -57,7 +57,6 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.window.embedding.ActivityEmbeddingController;
|
||||
import androidx.window.embedding.SplitRule;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -108,7 +107,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
private View mTwoPaneSuggestionView;
|
||||
private CategoryMixin mCategoryMixin;
|
||||
private Set<HomepageLoadedListener> mLoadedListeners;
|
||||
private ActivityEmbeddingController mActivityEmbeddingController;
|
||||
private boolean mIsEmbeddingActivityEnabled;
|
||||
private boolean mIsTwoPane;
|
||||
// A regular layout shows icons on homepage, whereas a simplified layout doesn't.
|
||||
@@ -200,8 +198,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
setupEdgeToEdge();
|
||||
setContentView(R.layout.settings_homepage_container);
|
||||
|
||||
mActivityEmbeddingController = ActivityEmbeddingController.getInstance(this);
|
||||
mIsTwoPane = mActivityEmbeddingController.isActivityEmbedded(this);
|
||||
mIsTwoPane = ActivityEmbeddingUtils.isAlreadyEmbedded(this);
|
||||
|
||||
updateAppBarMinHeight();
|
||||
initHomepageContainer();
|
||||
@@ -242,7 +239,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
|
||||
// Settings app may be launched on an existing task. Reset SplitPairRule of SubSettings here
|
||||
// to prevent SplitPairRule of an existing task applied on a new started Settings app.
|
||||
if (ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)
|
||||
if (mIsEmbeddingActivityEnabled
|
||||
&& (getIntent().getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
|
||||
initSplitPairRules();
|
||||
}
|
||||
@@ -284,7 +281,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
final boolean newTwoPaneState = mActivityEmbeddingController.isActivityEmbedded(this);
|
||||
final boolean newTwoPaneState = ActivityEmbeddingUtils.isAlreadyEmbedded(this);
|
||||
if (mIsTwoPane != newTwoPaneState) {
|
||||
mIsTwoPane = newTwoPaneState;
|
||||
updateHomepageAppBar();
|
||||
@@ -427,8 +424,9 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
}
|
||||
|
||||
private boolean shouldLaunchDeepLinkIntentToRight() {
|
||||
if (!FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)
|
||||
|| !ActivityEmbeddingUtils.isSettingsSplitEnabled(this)) {
|
||||
if (!ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
|
||||
|| !FeatureFlagUtils.isEnabled(this,
|
||||
FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -35,7 +35,6 @@ import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.android.internal.app.LocaleStore;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.RestrictedSettingsFragment;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
@@ -46,49 +45,19 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
public class LocaleDialogFragment extends InstrumentedDialogFragment {
|
||||
private static final String TAG = LocaleDialogFragment.class.getSimpleName();
|
||||
|
||||
static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 0;
|
||||
static final int DIALOG_NOT_AVAILABLE_LOCALE = 1;
|
||||
static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 1;
|
||||
static final int DIALOG_NOT_AVAILABLE_LOCALE = 2;
|
||||
|
||||
static final String ARG_DIALOG_TYPE = "arg_dialog_type";
|
||||
static final String ARG_TARGET_LOCALE = "arg_target_locale";
|
||||
static final String ARG_RESULT_RECEIVER = "arg_result_receiver";
|
||||
static final String ARG_SHOW_DIALOG = "arg_show_dialog";
|
||||
|
||||
private boolean mShouldKeepDialog;
|
||||
|
||||
public static LocaleDialogFragment newInstance() {
|
||||
return new LocaleDialogFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog
|
||||
*/
|
||||
public void show(
|
||||
@NonNull RestrictedSettingsFragment fragment,
|
||||
int dialogType,
|
||||
LocaleStore.LocaleInfo localeInfo) {
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
show(fragment, dialogType, localeInfo, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog
|
||||
*/
|
||||
public void show(
|
||||
@NonNull RestrictedSettingsFragment fragment,
|
||||
int dialogType,
|
||||
LocaleStore.LocaleInfo localeInfo,
|
||||
ResultReceiver resultReceiver) {
|
||||
FragmentManager manager = fragment.getChildFragmentManager();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_DIALOG_TYPE, dialogType);
|
||||
args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
|
||||
args.putParcelable(ARG_RESULT_RECEIVER, resultReceiver);
|
||||
|
||||
LocaleDialogFragment localeDialogFragment = new LocaleDialogFragment();
|
||||
localeDialogFragment.setArguments(args);
|
||||
localeDialogFragment.show(manager, TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
int dialogType = getArguments().getInt(ARG_DIALOG_TYPE);
|
||||
@@ -102,9 +71,29 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(ARG_SHOW_DIALOG, mShouldKeepDialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
LocaleDialogController controller = new LocaleDialogController(this);
|
||||
if (savedInstanceState != null) {
|
||||
Bundle arguments = getArguments();
|
||||
int type = arguments.getInt(ARG_DIALOG_TYPE);
|
||||
mShouldKeepDialog = savedInstanceState.getBoolean(ARG_SHOW_DIALOG, false);
|
||||
// Keep the dialog if user rotates the device, otherwise close the confirm system
|
||||
// default dialog only when user changes the locale.
|
||||
if (type == DIALOG_CONFIRM_SYSTEM_DEFAULT && !mShouldKeepDialog) {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
mShouldKeepDialog = true;
|
||||
LocaleListEditor parentFragment = (LocaleListEditor) getParentFragment();
|
||||
LocaleDialogController controller = getLocaleDialogController(getContext(), this,
|
||||
parentFragment);
|
||||
LocaleDialogController.DialogContent dialogContent = controller.getDialogContent();
|
||||
ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.locale_dialog, null);
|
||||
@@ -140,47 +129,57 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
|
||||
textView.setText(content);
|
||||
}
|
||||
|
||||
static class LocaleDialogController implements DialogInterface.OnClickListener {
|
||||
@VisibleForTesting
|
||||
LocaleDialogController getLocaleDialogController(Context context,
|
||||
LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) {
|
||||
return new LocaleDialogController(context, dialogFragment, parentFragment);
|
||||
}
|
||||
|
||||
class LocaleDialogController implements DialogInterface.OnClickListener {
|
||||
private final Context mContext;
|
||||
private final int mDialogType;
|
||||
private final LocaleStore.LocaleInfo mLocaleInfo;
|
||||
private final ResultReceiver mResultReceiver;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
|
||||
private LocaleListEditor mParent;
|
||||
|
||||
LocaleDialogController(
|
||||
@NonNull Context context, @NonNull LocaleDialogFragment dialogFragment) {
|
||||
@NonNull Context context, @NonNull LocaleDialogFragment dialogFragment,
|
||||
LocaleListEditor parentFragment) {
|
||||
mContext = context;
|
||||
Bundle arguments = dialogFragment.getArguments();
|
||||
mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
|
||||
mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(
|
||||
ARG_TARGET_LOCALE);
|
||||
mResultReceiver = (ResultReceiver) arguments.getParcelable(ARG_RESULT_RECEIVER);
|
||||
mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
|
||||
mMetricsFeatureProvider = FeatureFactory.getFactory(
|
||||
mContext).getMetricsFeatureProvider();
|
||||
mParent = parentFragment;
|
||||
}
|
||||
|
||||
LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment) {
|
||||
this(dialogFragment.getContext(), dialogFragment);
|
||||
LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment,
|
||||
LocaleListEditor parent) {
|
||||
this(dialogFragment.getContext(), dialogFragment, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mResultReceiver != null && mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
|
||||
if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
|
||||
int result = Activity.RESULT_CANCELED;
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
result = Activity.RESULT_OK;
|
||||
}
|
||||
Intent intent = new Intent();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
mResultReceiver.send(Activity.RESULT_OK, bundle);
|
||||
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
|
||||
mResultReceiver.send(Activity.RESULT_CANCELED, bundle);
|
||||
}
|
||||
intent.putExtras(bundle);
|
||||
mParent.onActivityResult(DIALOG_CONFIRM_SYSTEM_DEFAULT, result, intent);
|
||||
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE);
|
||||
}
|
||||
mShouldKeepDialog = false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
DialogContent getDialogContent() {
|
||||
DialogContent
|
||||
dialogContent = new DialogContent();
|
||||
DialogContent dialogContent = new DialogContent();
|
||||
switch (mDialogType) {
|
||||
case DIALOG_CONFIRM_SYSTEM_DEFAULT:
|
||||
dialogContent.mTitle = String.format(mContext.getString(
|
||||
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
package com.android.settings.localepicker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.LocaleList;
|
||||
import android.os.Looper;
|
||||
import android.os.ResultReceiver;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -52,15 +49,20 @@ class LocaleDragAndDropAdapter
|
||||
|
||||
private static final String TAG = "LocaleDragAndDropAdapter";
|
||||
private static final String CFGKEY_SELECTED_LOCALES = "selectedLocales";
|
||||
private static final String CFGKEY_DRAG_LOCALE = "dragLocales";
|
||||
private static final String CFGKEY_DRAG_LOCALES_TO_POSITION = "dragLocales_end";
|
||||
|
||||
private final Context mContext;
|
||||
private final ItemTouchHelper mItemTouchHelper;
|
||||
|
||||
private List<LocaleStore.LocaleInfo> mFeedItemList;
|
||||
private List<LocaleStore.LocaleInfo> mCacheItemList;
|
||||
private final ItemTouchHelper mItemTouchHelper;
|
||||
private RecyclerView mParentView = null;
|
||||
private LocaleListEditor mParent;
|
||||
private boolean mRemoveMode = false;
|
||||
private boolean mDragEnabled = true;
|
||||
private NumberFormat mNumberFormatter = NumberFormat.getNumberInstance();
|
||||
private LocaleStore.LocaleInfo mDragLocale;
|
||||
|
||||
class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener {
|
||||
private final LocaleDragCell mLocaleDragCell;
|
||||
@@ -87,8 +89,7 @@ class LocaleDragAndDropAdapter
|
||||
}
|
||||
}
|
||||
|
||||
LocaleDragAndDropAdapter(LocaleListEditor parent,
|
||||
List<LocaleStore.LocaleInfo> feedItemList) {
|
||||
LocaleDragAndDropAdapter(LocaleListEditor parent, List<LocaleStore.LocaleInfo> feedItemList) {
|
||||
mFeedItemList = feedItemList;
|
||||
mParent = parent;
|
||||
mCacheItemList = new ArrayList<>(feedItemList);
|
||||
@@ -202,6 +203,7 @@ class LocaleDragAndDropAdapter
|
||||
final LocaleStore.LocaleInfo saved = mFeedItemList.get(fromPosition);
|
||||
mFeedItemList.remove(fromPosition);
|
||||
mFeedItemList.add(toPosition, saved);
|
||||
mDragLocale = saved;
|
||||
} else {
|
||||
// TODO: It looks like sometimes the RecycleView tries to swap item -1
|
||||
// I did not see it in a while, but if it happens, investigate and file a bug.
|
||||
@@ -317,43 +319,20 @@ class LocaleDragAndDropAdapter
|
||||
});
|
||||
}
|
||||
|
||||
public void doTheUpdateWithMovingLocaleItem() {
|
||||
LocaleStore.LocaleInfo localeInfo = mFeedItemList.get(0);
|
||||
final LocaleDialogFragment fragment = LocaleDialogFragment.newInstance();
|
||||
if (!localeInfo.getLocale().equals(LocalePicker.getLocales().get(0))) {
|
||||
fragment.show(mParent,
|
||||
LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT,
|
||||
localeInfo,
|
||||
new ResultReceiver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
super.onReceiveResult(resultCode, resultData);
|
||||
int type = resultData.getInt(LocaleDialogFragment.ARG_DIALOG_TYPE);
|
||||
if (type == LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
doTheUpdate();
|
||||
if (!localeInfo.isTranslated()) {
|
||||
fragment.show(mParent,
|
||||
LocaleDialogFragment
|
||||
.DIALOG_NOT_AVAILABLE_LOCALE,
|
||||
localeInfo);
|
||||
}
|
||||
} else {
|
||||
if (!localeInfo.getLocale()
|
||||
.equals(mCacheItemList.get(0).getLocale())) {
|
||||
mFeedItemList = new ArrayList<>(mCacheItemList);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
mCacheItemList = new ArrayList<>(mFeedItemList);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doTheUpdate();
|
||||
public void notifyListChanged(LocaleStore.LocaleInfo localeInfo) {
|
||||
if (!localeInfo.getLocale().equals(mCacheItemList.get(0).getLocale())) {
|
||||
mFeedItemList = new ArrayList<>(mCacheItemList);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheItemList() {
|
||||
mCacheItemList = new ArrayList<>(mFeedItemList);
|
||||
}
|
||||
|
||||
public List<LocaleStore.LocaleInfo> getFeedItemList() {
|
||||
return mFeedItemList;
|
||||
}
|
||||
private void setDragEnabled(boolean enabled) {
|
||||
mDragEnabled = enabled;
|
||||
}
|
||||
@@ -373,6 +352,8 @@ class LocaleDragAndDropAdapter
|
||||
}
|
||||
}
|
||||
outInstanceState.putStringArrayList(CFGKEY_SELECTED_LOCALES, selectedLocales);
|
||||
// Save the dragged locale before rotation
|
||||
outInstanceState.putSerializable(CFGKEY_DRAG_LOCALE, mDragLocale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,18 +362,30 @@ class LocaleDragAndDropAdapter
|
||||
* (for instance when the device is rotated)
|
||||
*
|
||||
* @param savedInstanceState Bundle with the data saved by {@link #saveState(Bundle)}
|
||||
* @param isDialogShowing A flag indicating whether the dialog is showing or not.
|
||||
*/
|
||||
public void restoreState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null && mRemoveMode) {
|
||||
final ArrayList<String> selectedLocales =
|
||||
savedInstanceState.getStringArrayList(CFGKEY_SELECTED_LOCALES);
|
||||
if (selectedLocales == null || selectedLocales.isEmpty()) {
|
||||
return;
|
||||
public void restoreState(Bundle savedInstanceState, boolean isDialogShowing) {
|
||||
if (savedInstanceState != null) {
|
||||
if (mRemoveMode) {
|
||||
final ArrayList<String> selectedLocales =
|
||||
savedInstanceState.getStringArrayList(CFGKEY_SELECTED_LOCALES);
|
||||
if (selectedLocales == null || selectedLocales.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (LocaleStore.LocaleInfo li : mFeedItemList) {
|
||||
li.setChecked(selectedLocales.contains(li.getId()));
|
||||
}
|
||||
notifyItemRangeChanged(0, mFeedItemList.size());
|
||||
} else if (isDialogShowing) {
|
||||
// After rotation, the dragged position will be restored to original. Restore the
|
||||
// drag locale's original position to the top.
|
||||
mDragLocale = (LocaleStore.LocaleInfo) savedInstanceState.getSerializable(
|
||||
CFGKEY_DRAG_LOCALE);
|
||||
mFeedItemList.removeIf(
|
||||
localeInfo -> TextUtils.equals(localeInfo.getId(), mDragLocale.getId()));
|
||||
mFeedItemList.add(0, mDragLocale);
|
||||
notifyItemRangeChanged(0, mFeedItemList.size());
|
||||
}
|
||||
for (LocaleStore.LocaleInfo li : mFeedItemList) {
|
||||
li.setChecked(selectedLocales.contains(li.getId()));
|
||||
}
|
||||
notifyItemRangeChanged(0, mFeedItemList.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.settings.localepicker;
|
||||
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_LOCALE;
|
||||
|
||||
import static com.android.settings.localepicker.LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -32,12 +34,14 @@ import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -60,7 +64,7 @@ import java.util.Locale;
|
||||
* Drag-and-drop editor for the user-ordered locale lists.
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
public class LocaleListEditor extends RestrictedSettingsFragment implements View.OnTouchListener {
|
||||
|
||||
protected static final String INTENT_LOCALE_KEY = "localeInfo";
|
||||
private static final String CFGKEY_REMOVE_MODE = "localeRemoveMode";
|
||||
@@ -70,6 +74,8 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
|
||||
private static final String INDEX_KEY_ADD_LANGUAGE = "add_language";
|
||||
private static final String KEY_LANGUAGES_PICKER = "languages_picker";
|
||||
private static final String TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT = "dialog_confirm_system_default";
|
||||
private static final String TAG_DIALOG_NOT_AVAILABLE = "dialog_not_available_locale";
|
||||
|
||||
private LocaleDragAndDropAdapter mAdapter;
|
||||
private Menu mMenu;
|
||||
@@ -80,6 +86,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
|
||||
private LayoutPreference mLocalePickerPreference;
|
||||
private LocaleHelperPreferenceController mLocaleHelperPreferenceController;
|
||||
private FragmentManager mFragmentManager;
|
||||
|
||||
public LocaleListEditor() {
|
||||
super(DISALLOW_CONFIG_LOCALE);
|
||||
@@ -106,6 +113,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
LocaleStore.fillCache(this.getContext());
|
||||
final List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList();
|
||||
mAdapter = new LocaleDragAndDropAdapter(this, feedsList);
|
||||
mFragmentManager = getChildFragmentManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,7 +149,15 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
mShowingRemoveDialog = savedInstanceState.getBoolean(CFGKEY_REMOVE_DIALOG, false);
|
||||
}
|
||||
setRemoveMode(mRemoveMode);
|
||||
mAdapter.restoreState(savedInstanceState);
|
||||
|
||||
final LocaleDialogFragment dialogFragment =
|
||||
(LocaleDialogFragment) mFragmentManager.findFragmentByTag(
|
||||
TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
boolean isDialogShowing = false;
|
||||
if (dialogFragment != null && dialogFragment.isAdded()) {
|
||||
isDialogShowing = true;
|
||||
}
|
||||
mAdapter.restoreState(savedInstanceState, isDialogShowing);
|
||||
|
||||
if (mShowingRemoveDialog) {
|
||||
showRemoveLocaleWarningDialog();
|
||||
@@ -178,17 +194,32 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
LocaleStore.LocaleInfo localeInfo;
|
||||
if (requestCode == REQUEST_LOCALE_PICKER && resultCode == Activity.RESULT_OK
|
||||
&& data != null) {
|
||||
final LocaleStore.LocaleInfo localeInfo =
|
||||
(LocaleStore.LocaleInfo) data.getSerializableExtra(
|
||||
INTENT_LOCALE_KEY);
|
||||
|
||||
localeInfo = (LocaleStore.LocaleInfo) data.getSerializableExtra(INTENT_LOCALE_KEY);
|
||||
String preferencesTags = Settings.System.getString(
|
||||
getContext().getContentResolver(), Settings.System.LOCALE_PREFERENCES);
|
||||
|
||||
mAdapter.addLocale(mayAppendUnicodeTags(localeInfo, preferencesTags));
|
||||
updateVisibilityOfRemoveMenu();
|
||||
} else if (requestCode == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
|
||||
localeInfo = mAdapter.getFeedItemList().get(0);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
mAdapter.doTheUpdate();
|
||||
if (!localeInfo.isTranslated()) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE,
|
||||
LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE);
|
||||
args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
|
||||
LocaleDialogFragment localeDialogFragment = LocaleDialogFragment.newInstance();
|
||||
localeDialogFragment.setArguments(args);
|
||||
localeDialogFragment.show(mFragmentManager, TAG_DIALOG_NOT_AVAILABLE);
|
||||
}
|
||||
} else {
|
||||
mAdapter.notifyListChanged(localeInfo);
|
||||
}
|
||||
mAdapter.setCacheItemList();
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
@@ -332,6 +363,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
list.setNestedScrollingEnabled(false);
|
||||
mAdapter.setRecyclerView(list);
|
||||
list.setAdapter(mAdapter);
|
||||
list.setOnTouchListener(this);
|
||||
|
||||
mAddLanguage = layout.findViewById(R.id.add_language);
|
||||
mAddLanguage.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -348,6 +380,26 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP
|
||||
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
LocaleStore.LocaleInfo localeInfo = mAdapter.getFeedItemList().get(0);
|
||||
if (!localeInfo.getLocale().equals(LocalePicker.getLocales().get(0))) {
|
||||
final LocaleDialogFragment localeDialogFragment =
|
||||
LocaleDialogFragment.newInstance();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
|
||||
localeDialogFragment.setArguments(args);
|
||||
localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
} else {
|
||||
mAdapter.doTheUpdate();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide the "Remove" menu if there is only one locale in the list, show it otherwise
|
||||
// This is called when the menu is first created, and then one add / remove locale
|
||||
private void updateVisibilityOfRemoveMenu() {
|
||||
|
||||
@@ -34,15 +34,4 @@ class LocaleRecyclerView extends RecyclerView {
|
||||
public LocaleRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent e) {
|
||||
if (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
|
||||
if (adapter != null) {
|
||||
adapter.doTheUpdateWithMovingLocaleItem();
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
return;
|
||||
}
|
||||
|
||||
setupTetherPreference();
|
||||
|
||||
final Activity activity = getActivity();
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (adapter != null) {
|
||||
@@ -184,7 +186,6 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
}
|
||||
|
||||
setupTetherPreference();
|
||||
setTopIntroPreferenceTitle();
|
||||
|
||||
mDataSaverBackend.addListener(this);
|
||||
@@ -605,6 +606,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
if (mBluetoothPan.get() == null) {
|
||||
mBluetoothPan.set((BluetoothPan) proxy);
|
||||
updateBluetoothState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public class SlicePreferenceController extends BasePreferenceController implemen
|
||||
LiveData<Slice> mLiveData;
|
||||
@VisibleForTesting
|
||||
SlicePreference mSlicePreference;
|
||||
private boolean mIsObservering = false;
|
||||
private Uri mUri;
|
||||
|
||||
public SlicePreferenceController(Context context, String preferenceKey) {
|
||||
@@ -68,25 +69,31 @@ public class SlicePreferenceController extends BasePreferenceController implemen
|
||||
});
|
||||
|
||||
//TODO(b/120803703): figure out why we need to remove observer first
|
||||
mLiveData.removeObserver(this);
|
||||
removeLiveDataObserver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
if (mLiveData != null) {
|
||||
if (mLiveData != null && !mIsObservering) {
|
||||
mIsObservering = true;
|
||||
mLiveData.observeForever(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
if (mLiveData != null) {
|
||||
mLiveData.removeObserver(this);
|
||||
}
|
||||
removeLiveDataObserver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(Slice slice) {
|
||||
mSlicePreference.onSliceUpdated(slice);
|
||||
}
|
||||
|
||||
private void removeLiveDataObserver() {
|
||||
if (mLiveData != null && mIsObservering && mLiveData.hasActiveObservers()) {
|
||||
mIsObservering = false;
|
||||
mLiveData.removeObserver(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,25 +24,22 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHapClient;
|
||||
import android.bluetooth.BluetoothHearingAid;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settings.utils.ActivityControllerWrapper;
|
||||
import com.android.settingslib.bluetooth.BluetoothEventManager;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
@@ -55,11 +52,12 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
@@ -74,6 +72,9 @@ import java.util.Set;
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
|
||||
public class AccessibilityHearingAidPreferenceControllerTest {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||
private static final String TEST_DEVICE_ADDRESS_2 = "00:A2:A2:A2:A2:A2";
|
||||
private static final String TEST_DEVICE_NAME = "TEST_HEARING_AID_BT_DEVICE_NAME";
|
||||
@@ -82,7 +83,8 @@ public class AccessibilityHearingAidPreferenceControllerTest {
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private Activity mContext;
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private Preference mHearingAidPreference;
|
||||
private AccessibilityHearingAidPreferenceController mPreferenceController;
|
||||
private ShadowApplication mShadowApplication;
|
||||
@@ -106,11 +108,7 @@ public class AccessibilityHearingAidPreferenceControllerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mShadowApplication = ShadowApplication.getInstance();
|
||||
|
||||
mContext = spy((Activity) ActivityControllerWrapper.setup(
|
||||
Robolectric.buildActivity(Activity.class)).get());
|
||||
setupEnvironment();
|
||||
|
||||
mHearingAidPreference = new Preference(mContext);
|
||||
@@ -274,65 +272,6 @@ public class AccessibilityHearingAidPreferenceControllerTest {
|
||||
verify(mPreferenceController).launchBluetoothDeviceDetailSetting(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSupportHearingAidProfile_isAvailable() {
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext,
|
||||
HEARING_AID_PREFERENCE);
|
||||
mPreferenceController.setPreference(mHearingAidPreference);
|
||||
|
||||
assertThat(mPreferenceController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSupportHapClientProfile_isAvailable() {
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HAP_CLIENT);
|
||||
mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext,
|
||||
HEARING_AID_PREFERENCE);
|
||||
mPreferenceController.setPreference(mHearingAidPreference);
|
||||
|
||||
assertThat(mPreferenceController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNotSupportAnyHearingAidRelatedProfile_isNotAvailable() {
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext,
|
||||
HEARING_AID_PREFERENCE);
|
||||
mPreferenceController.setPreference(mHearingAidPreference);
|
||||
|
||||
assertThat(mPreferenceController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedHearingAidDevice_doNotReturnSubDevice() {
|
||||
when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager().isSubDevice(mBluetoothDevice))
|
||||
.thenReturn(true);
|
||||
|
||||
assertThat(mPreferenceController.getConnectedHearingAidDevice()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowAlertDialogCompat.class)
|
||||
public void onActiveDeviceChanged_hearingAidProfile_launchHearingAidPairingDialog() {
|
||||
final FragmentActivity mActivity = Robolectric.setupActivity(FragmentActivity.class);
|
||||
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
|
||||
HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
|
||||
HearingAidInfo.DeviceSide.SIDE_LEFT);
|
||||
mPreferenceController.setFragmentManager(mActivity.getSupportFragmentManager());
|
||||
|
||||
mPreferenceController.onActiveDeviceChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.HEARING_AID);
|
||||
|
||||
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(dialog.isShowing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onServiceConnected_onHearingAidProfileConnected_updateSummary() {
|
||||
when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests for {@link HearingAidAudioRoutingPreferenceController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class HearingAidAudioRoutingPreferenceControllerTest {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private HearingAidAudioRoutingPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mController = new HearingAidAudioRoutingPreferenceController(mContext, "test_key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_audioRoutingNotSupported_returnUnsupported() {
|
||||
FeatureFlagUtils.setEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, false);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_audioRoutingNotSupported_available() {
|
||||
FeatureFlagUtils.setEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.HapClientProfile;
|
||||
import com.android.settingslib.bluetooth.HearingAidProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/** Tests for {@link HearingAidHelper}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
|
||||
public class HearingAidHelperTest {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private HearingAidProfile mHearingAidProfile;
|
||||
@Mock
|
||||
private HapClientProfile mHapClientProfile;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private HearingAidHelper mHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter);
|
||||
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
|
||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
|
||||
when(mLocalBluetoothProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
|
||||
|
||||
mHelper = new HearingAidHelper(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isHearingAidSupported_supported_returnTrue() {
|
||||
mBluetoothAdapter.enable();
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
|
||||
assertThat(mHelper.isHearingAidSupported()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isHearingAidSupported_bluetoothOff_returnFalse() {
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
mBluetoothAdapter.disable();
|
||||
|
||||
assertThat(mHelper.isHearingAidSupported()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void isAllHearingAidRelatedProfilesReady_allReady_returnTrue() {
|
||||
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
|
||||
when(mHapClientProfile.isProfileReady()).thenReturn(true);
|
||||
|
||||
assertThat(mHelper.isAllHearingAidRelatedProfilesReady()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAllHearingAidRelatedProfilesReady_notFullReady_returnFalse() {
|
||||
when(mHearingAidProfile.isProfileReady()).thenReturn(false);
|
||||
when(mHapClientProfile.isProfileReady()).thenReturn(true);
|
||||
|
||||
assertThat(mHelper.isAllHearingAidRelatedProfilesReady()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedHearingAidDeviceList_oneDeviceAdded_getOneDevice() {
|
||||
mBluetoothAdapter.enable();
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
when(mHearingAidProfile.getConnectedDevices()).thenReturn(new ArrayList<>(
|
||||
Collections.singletonList(mBluetoothDevice)));
|
||||
|
||||
assertThat(mHelper.getConnectedHearingAidDeviceList().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedHearingAidDeviceList_oneSubDeviceAdded_getZeroDevice() {
|
||||
mBluetoothAdapter.enable();
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
when(mHearingAidProfile.getConnectedDevices()).thenReturn(new ArrayList<>(
|
||||
Collections.singletonList(mBluetoothDevice)));
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager().isSubDevice(
|
||||
mBluetoothDevice)).thenReturn(true);
|
||||
|
||||
assertThat(mHelper.getConnectedHearingAidDeviceList().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedHearingAidDevice_getExpectedCachedBluetoothDevice() {
|
||||
mBluetoothAdapter.enable();
|
||||
mShadowBluetoothAdapter.clearSupportedProfiles();
|
||||
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
|
||||
when(mHearingAidProfile.getConnectedDevices()).thenReturn(new ArrayList<>(
|
||||
Collections.singletonList(mBluetoothDevice)));
|
||||
|
||||
assertThat(mHelper.getConnectedHearingAidDevice()).isEqualTo(mCachedBluetoothDevice);
|
||||
assertThat(mCachedBluetoothDevice.getAddress()).isEqualTo(mBluetoothDevice.getAddress());
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -37,9 +38,15 @@ import androidx.preference.ListPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants;
|
||||
import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -50,11 +57,13 @@ import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Tests for {@link HearingDeviceAudioRoutingBasePreferenceController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
|
||||
public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
|
||||
@Rule
|
||||
@@ -63,8 +72,14 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
@Spy
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||
private static final String FAKE_KEY = "fake_key";
|
||||
private final ListPreference mListPreference = new ListPreference(mContext);
|
||||
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private AudioProductStrategy mAudioProductStrategyMedia;
|
||||
@Mock
|
||||
@@ -72,8 +87,10 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Spy
|
||||
private HearingAidAudioRoutingHelper mHelper = new HearingAidAudioRoutingHelper(mContext);
|
||||
private final ListPreference mListPreference = new ListPreference(mContext);
|
||||
private HearingAidAudioRoutingHelper mAudioRoutingHelper =
|
||||
new HearingAidAudioRoutingHelper(mContext);
|
||||
@Mock
|
||||
private HearingAidHelper mHearingAidHelper;
|
||||
private TestHearingDeviceAudioRoutingBasePreferenceController mController;
|
||||
|
||||
@Before
|
||||
@@ -83,19 +100,23 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
AudioDeviceInfo.TYPE_HEARING_AID,
|
||||
TEST_DEVICE_ADDRESS);
|
||||
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
|
||||
when(mHelper.getMatchedHearingDeviceAttributes(any())).thenReturn(hearingDeviceAttribute);
|
||||
doReturn(hearingDeviceAttribute).when(
|
||||
mAudioRoutingHelper).getMatchedHearingDeviceAttributes(any());
|
||||
when(mAudioProductStrategyMedia.getAudioAttributesForLegacyStreamType(
|
||||
AudioManager.STREAM_MUSIC))
|
||||
.thenReturn((new AudioAttributes.Builder()).build());
|
||||
when(mHelper.getAudioProductStrategies()).thenReturn(List.of(mAudioProductStrategyMedia));
|
||||
AudioManager.STREAM_MUSIC)).thenReturn((new AudioAttributes.Builder()).build());
|
||||
when(mAudioRoutingHelper.getAudioProductStrategies()).thenReturn(
|
||||
List.of(mAudioProductStrategyMedia));
|
||||
|
||||
mController = new TestHearingDeviceAudioRoutingBasePreferenceController(mContext, FAKE_KEY,
|
||||
mHelper);
|
||||
TestHearingDeviceAudioRoutingBasePreferenceController.setupForTesting(
|
||||
mCachedBluetoothDevice);
|
||||
mController = new TestHearingDeviceAudioRoutingBasePreferenceController(mContext,
|
||||
"test_key",
|
||||
mAudioRoutingHelper, mHearingAidHelper);
|
||||
mListPreference.setEntries(R.array.bluetooth_audio_routing_titles);
|
||||
mListPreference.setEntryValues(R.array.bluetooth_audio_routing_values);
|
||||
mListPreference.setSummary("%s");
|
||||
@@ -122,20 +143,21 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_noMatchedDeviceAttributes_notCallSetStrategies() {
|
||||
when(mHelper.getMatchedHearingDeviceAttributes(any())).thenReturn(null);
|
||||
when(mAudioRoutingHelper.getMatchedHearingDeviceAttributes(any())).thenReturn(null);
|
||||
|
||||
verify(mHelper, never()).setPreferredDeviceRoutingStrategies(any(), isNull(), anyInt());
|
||||
verify(mAudioRoutingHelper, never()).setPreferredDeviceRoutingStrategies(any(), isNull(),
|
||||
anyInt());
|
||||
}
|
||||
|
||||
private static class TestHearingDeviceAudioRoutingBasePreferenceController extends
|
||||
HearingDeviceAudioRoutingBasePreferenceController {
|
||||
|
||||
private static CachedBluetoothDevice sCachedBluetoothDevice;
|
||||
private static int sSavedRoutingValue;
|
||||
|
||||
TestHearingDeviceAudioRoutingBasePreferenceController(Context context,
|
||||
String preferenceKey, HearingAidAudioRoutingHelper helper) {
|
||||
super(context, preferenceKey, helper);
|
||||
String preferenceKey, HearingAidAudioRoutingHelper audioRoutingHelper,
|
||||
HearingAidHelper hearingAidHelper) {
|
||||
super(context, preferenceKey, audioRoutingHelper, hearingAidHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,11 +165,6 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
return new int[]{AudioAttributes.USAGE_MEDIA};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedBluetoothDevice getHearingDevice() {
|
||||
return sCachedBluetoothDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveRoutingValue(Context context, int routingValue) {
|
||||
sSavedRoutingValue = routingValue;
|
||||
@@ -157,9 +174,5 @@ public class HearingDeviceAudioRoutingBasePreferenceControllerTest {
|
||||
protected int restoreRoutingValue(Context context) {
|
||||
return sSavedRoutingValue;
|
||||
}
|
||||
|
||||
public static void setupForTesting(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
sCachedBluetoothDevice = cachedBluetoothDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
@@ -20,13 +20,19 @@ import static android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SE
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -35,39 +41,51 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ContactSearchPreferenceControllerTest {
|
||||
|
||||
private static final String PREF_KEY = "contacts_search";
|
||||
|
||||
@Mock
|
||||
private UserHandle mManagedUser;
|
||||
private static final int MANAGED_USER_ID = 10;
|
||||
|
||||
private Context mContext;
|
||||
private ContactSearchPreferenceController mController;
|
||||
private RestrictedSwitchPreference mPreference;
|
||||
|
||||
@Mock
|
||||
private UserHandle mManagedUser;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private UserInfo mUserInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new ContactSearchPreferenceController(mContext, PREF_KEY);
|
||||
mController.setManagedUser(mManagedUser);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
mPreference = spy(new RestrictedSwitchPreference(mContext));
|
||||
when(mUserInfo.isManagedProfile()).thenReturn(true);
|
||||
when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(0);
|
||||
when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser));
|
||||
when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID);
|
||||
mController = new ContactSearchPreferenceController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noManagedUser_DISABLED() {
|
||||
mController.setManagedUser(null);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(MANAGED_USER_ID);
|
||||
mController = new ContactSearchPreferenceController(mContext, PREF_KEY);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isNotEqualTo(ContactSearchPreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_hasManagedUser_AVAILABLE() {
|
||||
mController.setManagedUser(mManagedUser);
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(ContactSearchPreferenceController.AVAILABLE);
|
||||
}
|
||||
@@ -75,32 +93,96 @@ public class ContactSearchPreferenceControllerTest {
|
||||
@Test
|
||||
public void updateState_shouldRefreshContent() {
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier());
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, mManagedUser.getIdentifier());
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_preferenceShouldBeDisabled() {
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setDisabledByAdmin(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_shouldUpdateProviderValue() {
|
||||
mController.onPreferenceChange(mPreference, false);
|
||||
|
||||
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, mManagedUser.getIdentifier()))
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID))
|
||||
.isEqualTo(0);
|
||||
|
||||
mController.onPreferenceChange(mPreference, true);
|
||||
|
||||
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier()))
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID))
|
||||
.isEqualTo(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onQuietModeDisabled_preferenceEnabled() {
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onQuietModeEnabled_preferenceDisabledAndUnchecked() {
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterQuietModeTurnedOnAndOffWhenPreferenceChecked_toggleCheckedAndEnabled() {
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID);
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterQuietModeTurnedOnAndOffWhenPreferenceUnchecked_toggleUncheckedAndEnabled() {
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID);
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.accounts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LifecycleRegistry;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
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 java.util.Collections;
|
||||
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ManagedProfileQuietModeEnablerTest {
|
||||
private static final int MANAGED_USER_ID = 10;
|
||||
private Context mContext;
|
||||
private ManagedProfileQuietModeEnabler mQuietModeEnabler;
|
||||
private LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
|
||||
public LifecycleRegistry registry = new LifecycleRegistry(this);
|
||||
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
return registry;
|
||||
}
|
||||
};
|
||||
|
||||
@Mock
|
||||
private ManagedProfileQuietModeEnabler.QuietModeChangeListener mOnQuietModeChangeListener;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private UserHandle mManagedUser;
|
||||
@Mock
|
||||
private UserInfo mUserInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
when(mUserInfo.isManagedProfile()).thenReturn(true);
|
||||
when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(0);
|
||||
when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID);
|
||||
when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser));
|
||||
mQuietModeEnabler = new ManagedProfileQuietModeEnabler(mContext,
|
||||
mOnQuietModeChangeListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSetQuietMode_shouldRequestQuietModeEnabled() {
|
||||
mQuietModeEnabler.setQuietModeEnabled(false);
|
||||
verify(mUserManager).requestQuietModeEnabled(false, mManagedUser);
|
||||
mQuietModeEnabler.setQuietModeEnabled(true);
|
||||
verify(mUserManager).requestQuietModeEnabled(true, mManagedUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onIsQuietModeEnabled_shouldCallIsQuietModeEnabled() {
|
||||
assertThat(mQuietModeEnabler.isQuietModeEnabled()).isEqualTo(
|
||||
verify(mUserManager).isQuietModeEnabled(any()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onQuietModeChanged_listenerNotified() {
|
||||
mQuietModeEnabler.onStart(mLifecycleOwner);
|
||||
mContext.sendBroadcast(new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE).putExtra(
|
||||
Intent.EXTRA_USER_HANDLE, MANAGED_USER_ID));
|
||||
mContext.sendBroadcast(new Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE).putExtra(
|
||||
Intent.EXTRA_USER_HANDLE, MANAGED_USER_ID));
|
||||
verify(mOnQuietModeChangeListener, times(2)).onQuietModeChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_shouldRegisterReceiver() {
|
||||
mQuietModeEnabler.onStart(mLifecycleOwner);
|
||||
verify(mContext).registerReceiver(eq(mQuietModeEnabler.mReceiver), any(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_shouldUnregisterReceiver() {
|
||||
// register it first
|
||||
mContext.registerReceiver(mQuietModeEnabler.mReceiver, null,
|
||||
Context.RECEIVER_EXPORTED/*UNAUDITED*/);
|
||||
|
||||
mQuietModeEnabler.onStop(mLifecycleOwner);
|
||||
verify(mContext).unregisterReceiver(mQuietModeEnabler.mReceiver);
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,18 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -38,43 +38,51 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class WorkModePreferenceControllerTest {
|
||||
|
||||
private static final String PREF_KEY = "work_mode";
|
||||
private static final int MANAGED_USER_ID = 10;
|
||||
|
||||
private Context mContext;
|
||||
private WorkModePreferenceController mController;
|
||||
private MainSwitchPreference mPreference;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private UserHandle mManagedUser;
|
||||
|
||||
private Context mContext;
|
||||
private WorkModePreferenceController mController;
|
||||
private SwitchPreference mPreference;
|
||||
@Mock
|
||||
private UserInfo mUserInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
mPreference = new MainSwitchPreference(mContext);
|
||||
when(mUserInfo.isManagedProfile()).thenReturn(true);
|
||||
when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(0);
|
||||
when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser));
|
||||
when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID);
|
||||
mController = new WorkModePreferenceController(mContext, PREF_KEY);
|
||||
mController.setManagedUser(mManagedUser);
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noManagedUser_DISABLED() {
|
||||
mController.setManagedUser(null);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(MANAGED_USER_ID);
|
||||
mController = new WorkModePreferenceController(mContext, PREF_KEY);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isNotEqualTo(WorkModePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_hasManagedUser_AVAILABLE() {
|
||||
mController.setManagedUser(mManagedUser);
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(WorkModePreferenceController.AVAILABLE);
|
||||
}
|
||||
@@ -83,41 +91,29 @@ public class WorkModePreferenceControllerTest {
|
||||
public void updateState_shouldRefreshContent() {
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
assertThat(mPreference.getSummary())
|
||||
.isEqualTo(mContext.getText(R.string.work_mode_on_summary));
|
||||
|
||||
when(mUserManager.isQuietModeEnabled(any(UserHandle.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
assertThat(mPreference.getSummary())
|
||||
.isEqualTo(mContext.getText(R.string.work_mode_off_summary));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_shouldRequestQuietModeEnabled() {
|
||||
mController.setPreference(mPreference);
|
||||
|
||||
mController.onPreferenceChange(mPreference, true);
|
||||
|
||||
verify(mUserManager).requestQuietModeEnabled(false, mManagedUser);
|
||||
|
||||
mController.onPreferenceChange(mPreference, false);
|
||||
|
||||
verify(mUserManager).requestQuietModeEnabled(true, mManagedUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_shouldRegisterReceiver() {
|
||||
mController.onStart();
|
||||
verify(mContext).registerReceiver(eq(mController.mReceiver), any(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_shouldUnregisterReceiver() {
|
||||
// register it first
|
||||
mContext.registerReceiver(mController.mReceiver, null,
|
||||
Context.RECEIVER_EXPORTED/*UNAUDITED*/);
|
||||
|
||||
mController.onStop();
|
||||
verify(mContext).unregisterReceiver(mController.mReceiver);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.bluetooth;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsAudioRoutingController.KEY_AUDIO_ROUTING;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsAudioRoutingController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsAudioRoutingControllerTest extends
|
||||
BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
|
||||
|
||||
private BluetoothDetailsAudioRoutingController mController;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
mController = new BluetoothDetailsAudioRoutingController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
final PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
|
||||
preferenceCategory.setKey(mController.getPreferenceKey());
|
||||
mScreen.addPreference(preferenceCategory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isHearingAidDevice_available() {
|
||||
FeatureFlagUtils.setEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true);
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isNotHearingAidDevice_notAvailable() {
|
||||
FeatureFlagUtils.setEnabled(mContext,
|
||||
FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true);
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(false);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void init_isHearingAidDevice_expectedAudioRoutingPreference() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
|
||||
|
||||
mController.init(mScreen);
|
||||
final Preference preference = mScreen.findPreference(KEY_AUDIO_ROUTING);
|
||||
final String address = preference.getExtras().getString(
|
||||
BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS);
|
||||
final String fragment = preference.getFragment();
|
||||
|
||||
assertThat(address).isEqualTo(TEST_ADDRESS);
|
||||
assertThat(fragment).isEqualTo(BluetoothDetailsAudioRoutingFragment.class.getName());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.bluetooth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsAudioRoutingFragment}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothUtils.class})
|
||||
public class BluetoothDetailsAudioRoutingFragmentTest {
|
||||
|
||||
@Rule
|
||||
public MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private BluetoothDetailsAudioRoutingFragment mFragment;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private LocalBluetoothAdapter mLocalBluetoothAdapter;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
setupEnvironment();
|
||||
|
||||
when(mLocalBluetoothAdapter.getRemoteDevice(TEST_ADDRESS)).thenReturn(mBluetoothDevice);
|
||||
when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
|
||||
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
|
||||
|
||||
mFragment = new BluetoothDetailsAudioRoutingFragment();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAttach_setArgumentsWithAddress_expectedCachedDeviceWithAddress() {
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
|
||||
mFragment.setArguments(args);
|
||||
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
assertThat(mFragment.mCachedDevice.getAddress()).isEqualTo(TEST_ADDRESS);
|
||||
}
|
||||
|
||||
private void setupEnvironment() {
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -57,6 +58,7 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
@@ -199,6 +201,27 @@ public class DashboardFeatureProviderImplTest {
|
||||
assertThat(observers.get(0).getUri().toString()).isEqualTo(SWITCH_URI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindPreference_providerTileWithPendingIntent_shouldBindIntent() {
|
||||
final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
|
||||
Bundle metaData = new Bundle();
|
||||
metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
|
||||
metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
|
||||
metaData.putInt(META_DATA_KEY_ORDER, 10);
|
||||
metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
|
||||
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData);
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(RuntimeEnvironment.application, 0, new Intent("test"), 0);
|
||||
tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent);
|
||||
|
||||
mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
|
||||
preference, tile, "123", Preference.DEFAULT_ORDER);
|
||||
|
||||
assertThat(preference.getFragment()).isNull();
|
||||
assertThat(preference.getOnPreferenceClickListener()).isNotNull();
|
||||
assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindPreference_noFragmentMetadata_shouldBindIntent() {
|
||||
final Preference preference = new Preference(RuntimeEnvironment.application);
|
||||
@@ -630,6 +653,55 @@ public class DashboardFeatureProviderImplTest {
|
||||
assertThat(launchIntent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickPreference_providerTileWithPendingIntent_singleUser_executesPendingIntent() {
|
||||
final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
|
||||
Bundle metaData = new Bundle();
|
||||
metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
|
||||
metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
|
||||
metaData.putInt(META_DATA_KEY_ORDER, 10);
|
||||
metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
|
||||
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData);
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(RuntimeEnvironment.application, 0, new Intent("test"), 0);
|
||||
tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent);
|
||||
|
||||
mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
|
||||
preference, tile, "123", Preference.DEFAULT_ORDER);
|
||||
preference.performClick();
|
||||
|
||||
Intent nextStartedActivity =
|
||||
Shadows.shadowOf(RuntimeEnvironment.application).peekNextStartedActivity();
|
||||
assertThat(nextStartedActivity).isNotNull();
|
||||
assertThat(nextStartedActivity.getAction()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickPreference_providerTileWithPendingIntent_multiUser_showsProfileDialog() {
|
||||
final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
|
||||
Bundle metaData = new Bundle();
|
||||
metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
|
||||
metaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
|
||||
metaData.putInt(META_DATA_KEY_ORDER, 10);
|
||||
metaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
|
||||
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, metaData);
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(RuntimeEnvironment.application, 0, new Intent("test"), 0);
|
||||
tile.pendingIntentMap.put(UserHandle.CURRENT, pendingIntent);
|
||||
tile.pendingIntentMap.put(new UserHandle(10), pendingIntent);
|
||||
|
||||
mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon,
|
||||
preference, tile, "123", Preference.DEFAULT_ORDER);
|
||||
preference.performClick();
|
||||
|
||||
Fragment dialogFragment =
|
||||
mActivity.getSupportFragmentManager().findFragmentByTag("select_profile");
|
||||
assertThat(dialogFragment).isNotNull();
|
||||
Intent nextStartedActivity =
|
||||
Shadows.shadowOf(RuntimeEnvironment.application).peekNextStartedActivity();
|
||||
assertThat(nextStartedActivity).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openTileIntent_profileSelectionDialog_shouldShow() {
|
||||
ShadowUserManager.getShadow().addUser(10, "Someone", 0);
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
package com.android.settings.dashboard;
|
||||
|
||||
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.DASHBOARD_CONTAINER;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -30,6 +32,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -38,15 +41,18 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.preference.PreferenceManager.OnActivityResultListener;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.slices.BlockingSlicePrefController;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
@@ -57,6 +63,7 @@ import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
|
||||
import com.android.settingslib.drawer.ActivityTile;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.ProviderTile;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@@ -177,6 +184,43 @@ public class DashboardFragmentTest {
|
||||
verify(mTestFragment.mScreen, times(2)).addPreference(nullable(Preference.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayTilesAsPreference_withGroup_shouldAddTilesIntoGroup() {
|
||||
final ProviderInfo providerInfo = new ProviderInfo();
|
||||
providerInfo.packageName = "pkg";
|
||||
providerInfo.name = "provider";
|
||||
providerInfo.authority = "authority";
|
||||
final Bundle groupTileMetaData = new Bundle();
|
||||
groupTileMetaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_group_key");
|
||||
ProviderTile groupTile = new ProviderTile(providerInfo, mDashboardCategory.key,
|
||||
groupTileMetaData);
|
||||
mDashboardCategory.addTile(groupTile);
|
||||
|
||||
final Bundle subTileMetaData = new Bundle();
|
||||
subTileMetaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key3");
|
||||
subTileMetaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "injected_tile_group_key");
|
||||
subTileMetaData.putParcelable(
|
||||
META_DATA_PREFERENCE_PENDING_INTENT,
|
||||
PendingIntent.getActivity(mContext, 0, new Intent(), 0));
|
||||
ProviderTile subTile = new ProviderTile(providerInfo, mDashboardCategory.key,
|
||||
subTileMetaData);
|
||||
mDashboardCategory.addTile(subTile);
|
||||
|
||||
PreferenceCategory groupPreference = mock(PreferenceCategory.class);
|
||||
when(mFakeFeatureFactory.dashboardFeatureProvider
|
||||
.getTilesForCategory(nullable(String.class)))
|
||||
.thenReturn(mDashboardCategory);
|
||||
when(mFakeFeatureFactory.dashboardFeatureProvider
|
||||
.getDashboardKeyForTile(any(Tile.class)))
|
||||
.then(invocation -> ((Tile) invocation.getArgument(0)).getKey(mContext));
|
||||
when(mTestFragment.mScreen.findPreference("injected_tile_group_key"))
|
||||
.thenReturn(groupPreference);
|
||||
mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
|
||||
|
||||
verify(mTestFragment.mScreen, times(3)).addPreference(nullable(Preference.class));
|
||||
verify(groupPreference).addPreference(nullable(Preference.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayTilesAsPreference_shouldNotAddTilesWithoutIntent() {
|
||||
mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
|
||||
@@ -351,6 +395,16 @@ public class DashboardFragmentTest {
|
||||
assertThat(pref).isInstanceOf(SwitchPreference.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreference_isActivityTile_returnPreference() {
|
||||
final Preference pref = mTestFragment.createPreference(mActivityTile);
|
||||
|
||||
assertThat(pref).isInstanceOf(Preference.class);
|
||||
assertThat(pref).isNotInstanceOf(PrimarySwitchPreference.class);
|
||||
assertThat(pref).isNotInstanceOf(SwitchPreference.class);
|
||||
assertThat(pref.getWidgetLayoutResource()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreference_isActivityTileAndHasSwitch_returnPrimarySwitchPreference() {
|
||||
mActivityTile.getMetaData().putString(META_DATA_PREFERENCE_SWITCH_URI, "uri");
|
||||
@@ -360,6 +414,64 @@ public class DashboardFragmentTest {
|
||||
assertThat(pref).isInstanceOf(PrimarySwitchPreference.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreference_isProviderTileWithPendingIntent_returnPreferenceWithIcon() {
|
||||
final ProviderInfo providerInfo = new ProviderInfo();
|
||||
providerInfo.packageName = "pkg";
|
||||
providerInfo.name = "provider";
|
||||
providerInfo.authority = "authority";
|
||||
final Bundle metaData = new Bundle();
|
||||
metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2");
|
||||
ProviderTile providerTile = new ProviderTile(providerInfo, mDashboardCategory.key,
|
||||
metaData);
|
||||
providerTile.pendingIntentMap.put(
|
||||
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
|
||||
|
||||
final Preference pref = mTestFragment.createPreference(providerTile);
|
||||
|
||||
assertThat(pref).isInstanceOf(Preference.class);
|
||||
assertThat(pref).isNotInstanceOf(PrimarySwitchPreference.class);
|
||||
assertThat(pref).isNotInstanceOf(SwitchPreference.class);
|
||||
assertThat(pref.getWidgetLayoutResource())
|
||||
.isEqualTo(R.layout.preference_external_action_icon);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreference_isProviderTileWithPendingIntentAndSwitch_returnPrimarySwitch() {
|
||||
mProviderTile.pendingIntentMap.put(
|
||||
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
|
||||
|
||||
final Preference pref = mTestFragment.createPreference(mProviderTile);
|
||||
|
||||
assertThat(pref).isInstanceOf(PrimarySwitchPreference.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreference_isGroupTile_returnPreferenceCategory_logTileAdded() {
|
||||
final ProviderInfo providerInfo = new ProviderInfo();
|
||||
providerInfo.packageName = "pkg";
|
||||
providerInfo.name = "provider";
|
||||
providerInfo.authority = "authority";
|
||||
final Bundle metaData = new Bundle();
|
||||
metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2");
|
||||
ProviderTile providerTile =
|
||||
new ProviderTile(providerInfo, mDashboardCategory.key, metaData);
|
||||
MetricsFeatureProvider metricsFeatureProvider =
|
||||
mFakeFeatureFactory.getMetricsFeatureProvider();
|
||||
when(metricsFeatureProvider.getAttribution(any())).thenReturn(123);
|
||||
|
||||
final Preference pref = mTestFragment.createPreference(providerTile);
|
||||
|
||||
assertThat(pref).isInstanceOf(PreferenceCategory.class);
|
||||
verify(metricsFeatureProvider)
|
||||
.action(
|
||||
123,
|
||||
SettingsEnums.ACTION_SETTINGS_GROUP_TILE_ADDED_TO_SCREEN,
|
||||
mTestFragment.getMetricsCategory(),
|
||||
"injected_tile_key2",
|
||||
0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_test() {
|
||||
final int requestCode = 10;
|
||||
|
||||
@@ -24,7 +24,9 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
@@ -118,6 +120,28 @@ public class ProfileSelectDialogTest {
|
||||
verify(mUserManager, times(1)).getUserInfo(CLONE_USER.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePendingIntentsIfNeeded_removesUsersWithNoPendingIntentsAndCloneProfile() {
|
||||
final UserInfo userInfo = new UserInfo(CLONE_USER.getIdentifier(), "clone_user", null,
|
||||
UserInfo.FLAG_PROFILE, UserManager.USER_TYPE_PROFILE_CLONE);
|
||||
when(mUserManager.getUserInfo(CLONE_USER.getIdentifier())).thenReturn(userInfo);
|
||||
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
|
||||
tile.userHandle.add(CLONE_USER);
|
||||
tile.userHandle.add(NORMAL_USER);
|
||||
tile.userHandle.add(new UserHandle(10));
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
|
||||
tile.pendingIntentMap.put(CLONE_USER, pendingIntent);
|
||||
tile.pendingIntentMap.put(NORMAL_USER, pendingIntent);
|
||||
|
||||
ProfileSelectDialog.updatePendingIntentsIfNeeded(mContext, tile);
|
||||
|
||||
assertThat(tile.userHandle).hasSize(1);
|
||||
assertThat(tile.userHandle).containsExactly(NORMAL_USER);
|
||||
assertThat(tile.pendingIntentMap).hasSize(1);
|
||||
assertThat(tile.pendingIntentMap).containsKey(NORMAL_USER);
|
||||
verify(mUserManager, times(1)).getUserInfo(CLONE_USER.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDialog_showsCorrectTitle() {
|
||||
mContext.setTheme(R.style.Theme_AppCompat);
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.android.internal.os.BatteryStatsHistoryIterator;
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.widget.UsageView;
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.fuelgauge.Estimate;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -163,26 +162,6 @@ public class BatteryInfoTest {
|
||||
assertThat(info2.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBatteryInfo_basedOnUsageTrueLessThanSevenMinutes_usesCorrectString() {
|
||||
Estimate estimate = new Estimate(Duration.ofMinutes(7).toMillis(),
|
||||
true /* isBasedOnUsage */,
|
||||
1000 /* averageDischargeTime */);
|
||||
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
|
||||
mBatteryUsageStats, estimate, SystemClock.elapsedRealtime() * 1000,
|
||||
false /* shortString */);
|
||||
BatteryInfo info2 = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
|
||||
mBatteryUsageStats, estimate, SystemClock.elapsedRealtime() * 1000,
|
||||
true /* shortString */);
|
||||
|
||||
// These should be identical in either case
|
||||
assertThat(info.remainingLabel.toString()).isEqualTo(
|
||||
mContext.getString(R.string.power_remaining_duration_only_shutdown_imminent));
|
||||
assertThat(info2.remainingLabel.toString()).isEqualTo(
|
||||
mContext.getString(R.string.power_remaining_duration_only_shutdown_imminent));
|
||||
assertThat(info2.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getBatteryInfo_MoreThanOneDay_suggestionLabelIsCorrectString() {
|
||||
@@ -196,25 +175,6 @@ public class BatteryInfoTest {
|
||||
assertThat(info.suggestionLabel).doesNotContain(BATTERY_RUN_OUT_PREFIX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
testGetBatteryInfo_basedOnUsageTrueBetweenSevenAndFifteenMinutes_usesCorrectString() {
|
||||
Estimate estimate = new Estimate(Duration.ofMinutes(10).toMillis(),
|
||||
true /* isBasedOnUsage */,
|
||||
1000 /* averageDischargeTime */);
|
||||
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
|
||||
mBatteryUsageStats, estimate, SystemClock.elapsedRealtime() * 1000,
|
||||
false /* shortString */);
|
||||
|
||||
// Check that strings are showing less than 15 minutes remaining regardless of exact time.
|
||||
assertThat(info.chargeLabel.toString()).isEqualTo(
|
||||
mContext.getString(R.string.power_remaining_less_than_duration,
|
||||
FIFTEEN_MIN_FORMATTED, TEST_BATTERY_LEVEL_10));
|
||||
assertThat(info.remainingLabel.toString()).isEqualTo(
|
||||
mContext.getString(R.string.power_remaining_less_than_duration_only,
|
||||
FIFTEEN_MIN_FORMATTED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBatteryInfo_basedOnUsageFalse_usesDefaultString() {
|
||||
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
|
||||
|
||||
@@ -18,19 +18,32 @@ package com.android.settings.localepicker;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.android.internal.app.LocaleStore;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowActivityManager;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -45,22 +58,42 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowAlertDialogCompat.class)
|
||||
@Config(shadows = {ShadowAlertDialogCompat.class, ShadowActivityManager.class})
|
||||
public class LocaleListEditorTest {
|
||||
|
||||
private static final String ARG_DIALOG_TYPE = "arg_dialog_type";
|
||||
private static final String TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT = "dialog_confirm_system_default";
|
||||
private static final String TAG_DIALOG_NOT_AVAILABLE = "dialog_not_available_locale";
|
||||
private static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 1;
|
||||
private static final int REQUEST_CONFIRM_SYSTEM_DEFAULT = 1;
|
||||
|
||||
private LocaleListEditor mLocaleListEditor;
|
||||
|
||||
private Context mContext;
|
||||
private FragmentActivity mActivity;
|
||||
private List mLocaleList;
|
||||
private Intent mIntent = new Intent();
|
||||
|
||||
@Mock
|
||||
private LocaleDragAndDropAdapter mAdapter;
|
||||
@Mock
|
||||
private LocaleStore.LocaleInfo mLocaleInfo;
|
||||
@Mock
|
||||
private FragmentManager mFragmentManager;
|
||||
@Mock
|
||||
private FragmentTransaction mFragmentTransaction;
|
||||
@Mock
|
||||
private View mView;
|
||||
@Mock
|
||||
private IActivityManager mActivityService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mLocaleListEditor = spy(new LocaleListEditor());
|
||||
@@ -74,6 +107,8 @@ public class LocaleListEditorTest {
|
||||
ReflectionHelpers.setField(mLocaleListEditor, "mUserManager",
|
||||
RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE));
|
||||
ReflectionHelpers.setField(mLocaleListEditor, "mAdapter", mAdapter);
|
||||
ReflectionHelpers.setField(mLocaleListEditor, "mFragmentManager", mFragmentManager);
|
||||
when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
|
||||
FakeFeatureFactory.setupForTest();
|
||||
}
|
||||
|
||||
@@ -174,4 +209,65 @@ public class LocaleListEditorTest {
|
||||
assertThat(result.getLocale().getUnicodeLocaleType("fw")).isEqualTo("wed");
|
||||
assertThat(result.getLocale().getUnicodeLocaleType("mu")).isEqualTo("celsius");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_ResultCodeIsOk_showNotAvailableDialog() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
mIntent.putExtras(bundle);
|
||||
setUpLocaleConditions();
|
||||
mLocaleListEditor.onActivityResult(REQUEST_CONFIRM_SYSTEM_DEFAULT, Activity.RESULT_OK,
|
||||
mIntent);
|
||||
|
||||
verify(mFragmentTransaction).add(any(LocaleDialogFragment.class),
|
||||
eq(TAG_DIALOG_NOT_AVAILABLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_ResultCodeIsCancel_notifyAdapterListChanged() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
mIntent.putExtras(bundle);
|
||||
setUpLocaleConditions();
|
||||
mLocaleListEditor.onActivityResult(REQUEST_CONFIRM_SYSTEM_DEFAULT, Activity.RESULT_CANCELED,
|
||||
mIntent);
|
||||
|
||||
verify(mAdapter).notifyListChanged(mLocaleInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouch_dragDifferentLocaleToTop_showConfirmDialog() throws Exception {
|
||||
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
|
||||
setUpLocaleConditions();
|
||||
final Configuration config = new Configuration();
|
||||
config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
|
||||
when(mActivityService.getConfiguration()).thenReturn(config);
|
||||
when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
|
||||
mLocaleListEditor.onTouch(mView, event);
|
||||
|
||||
verify(mFragmentTransaction).add(any(LocaleDialogFragment.class),
|
||||
eq(TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouch_dragSameLocaleToTop_updateAdapter() throws Exception {
|
||||
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
|
||||
setUpLocaleConditions();
|
||||
final Configuration config = new Configuration();
|
||||
config.setLocales((LocaleList.forLanguageTags("en-US,zh-TW")));
|
||||
when(mActivityService.getConfiguration()).thenReturn(config);
|
||||
when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
|
||||
mLocaleListEditor.onTouch(mView, event);
|
||||
|
||||
verify(mAdapter).doTheUpdate();
|
||||
}
|
||||
|
||||
private void setUpLocaleConditions() {
|
||||
ShadowActivityManager.setService(mActivityService);
|
||||
mLocaleList = new ArrayList<>();
|
||||
mLocaleList.add(mLocaleInfo);
|
||||
when(mLocaleInfo.getFullNameNative()).thenReturn("English");
|
||||
when(mLocaleInfo.getLocale()).thenReturn(LocaleList.forLanguageTags("en-US").get(0));
|
||||
when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.android.settings.slices;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
@@ -43,11 +45,11 @@ public class SlicePreferenceControllerTest {
|
||||
private LiveData<Slice> mLiveData;
|
||||
@Mock
|
||||
private SlicePreference mSlicePreference;
|
||||
|
||||
private Context mContext;
|
||||
private SlicePreferenceController mController;
|
||||
private Uri mUri;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -78,10 +80,30 @@ public class SlicePreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void onStop_unregisterObserver() {
|
||||
when(mLiveData.hasActiveObservers()).thenReturn(true);
|
||||
mController.onStart();
|
||||
|
||||
mController.onStop();
|
||||
verify(mLiveData).removeObserver(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_noActiveObservers_notUnregisterObserver() {
|
||||
when(mLiveData.hasActiveObservers()).thenReturn(false);
|
||||
mController.onStart();
|
||||
|
||||
mController.onStop();
|
||||
verify(mLiveData, never()).removeObserver(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_notRegisterObserver_notUnregisterObserver() {
|
||||
when(mLiveData.hasActiveObservers()).thenReturn(true);
|
||||
|
||||
mController.onStop();
|
||||
verify(mLiveData, never()).removeObserver(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_nullSlice_updateSlice() {
|
||||
mController.onChanged(null);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package com.android.settings.localepicker;
|
||||
|
||||
import static com.android.settings.localepicker.LocaleDialogFragment.ARG_DIALOG_TYPE;
|
||||
import static com.android.settings.localepicker.LocaleDialogFragment.ARG_RESULT_RECEIVER;
|
||||
import static com.android.settings.localepicker.LocaleDialogFragment.ARG_TARGET_LOCALE;
|
||||
import static com.android.settings.localepicker.LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -27,12 +27,9 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -55,6 +52,7 @@ public class LocaleDialogFragmentTest {
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private Context mContext;
|
||||
private LocaleListEditor mLocaleListEditor;
|
||||
private LocaleDialogFragment mDialogFragment;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
|
||||
@@ -62,30 +60,30 @@ public class LocaleDialogFragmentTest {
|
||||
public void setUp() throws Exception {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mDialogFragment = new LocaleDialogFragment();
|
||||
mLocaleListEditor = spy(new LocaleListEditor());
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
}
|
||||
|
||||
private void setArgument(
|
||||
int type, ResultReceiver receiver) {
|
||||
private void setArgument(int type) {
|
||||
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(Locale.ENGLISH);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_DIALOG_TYPE, type);
|
||||
args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
|
||||
args.putParcelable(ARG_RESULT_RECEIVER, receiver);
|
||||
mDialogFragment.setArguments(args);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDialogContent_confirmSystemDefault_has2ButtonText() {
|
||||
setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, null);
|
||||
setArgument(DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
LocaleDialogFragment.LocaleDialogController controller =
|
||||
new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
|
||||
mDialogFragment.getLocaleDialogController(mContext, mDialogFragment,
|
||||
mLocaleListEditor);
|
||||
|
||||
LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
|
||||
controller.getDialogContent();
|
||||
|
||||
assertEquals(ResourcesUtils.getResourcesString(
|
||||
mContext, "button_label_confirmation_of_system_locale_change"),
|
||||
mContext, "button_label_confirmation_of_system_locale_change"),
|
||||
dialogContent.mPositiveButton);
|
||||
assertEquals(ResourcesUtils.getResourcesString(mContext, "cancel"),
|
||||
dialogContent.mNegativeButton);
|
||||
@@ -93,9 +91,10 @@ public class LocaleDialogFragmentTest {
|
||||
|
||||
@Test
|
||||
public void getDialogContent_unavailableLocale_has1ButtonText() {
|
||||
setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE, null);
|
||||
setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE);
|
||||
LocaleDialogFragment.LocaleDialogController controller =
|
||||
new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
|
||||
mDialogFragment.getLocaleDialogController(mContext, mDialogFragment,
|
||||
mLocaleListEditor);
|
||||
|
||||
LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
|
||||
controller.getDialogContent();
|
||||
@@ -105,38 +104,9 @@ public class LocaleDialogFragmentTest {
|
||||
assertTrue(dialogContent.mNegativeButton.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_clickPositiveButton_sendOK() {
|
||||
ResultReceiver resultReceiver = spy(new ResultReceiver(null));
|
||||
setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, resultReceiver);
|
||||
LocaleDialogFragment.LocaleDialogController controller =
|
||||
new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
|
||||
|
||||
controller.onClick(null, DialogInterface.BUTTON_POSITIVE);
|
||||
|
||||
verify(resultReceiver).send(eq(Activity.RESULT_OK), any());
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_clickNegativeButton_sendCancel() {
|
||||
ResultReceiver resultReceiver = spy(new ResultReceiver(null));
|
||||
setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, resultReceiver);
|
||||
LocaleDialogFragment.LocaleDialogController controller =
|
||||
new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
|
||||
|
||||
controller.onClick(null, DialogInterface.BUTTON_NEGATIVE);
|
||||
|
||||
verify(resultReceiver).send(eq(Activity.RESULT_CANCELED), any());
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMetricsCategory_systemLocaleChange() {
|
||||
setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, null);
|
||||
|
||||
setArgument(DIALOG_CONFIRM_SYSTEM_DEFAULT);
|
||||
int result = mDialogFragment.getMetricsCategory();
|
||||
|
||||
assertEquals(SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE, result);
|
||||
@@ -144,8 +114,7 @@ public class LocaleDialogFragmentTest {
|
||||
|
||||
@Test
|
||||
public void getMetricsCategory_unavailableLocale() {
|
||||
setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE, null);
|
||||
|
||||
setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE);
|
||||
int result = mDialogFragment.getMetricsCategory();
|
||||
|
||||
assertEquals(SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE, result);
|
||||
|
||||
Reference in New Issue
Block a user