Snap for 12368321 from 428829bfc5
to 24Q4-release
Change-Id: I657935e185a2cf0d3543c26f0e553ea87fcc5ec3
This commit is contained in:
@@ -94,8 +94,10 @@ android_library {
|
|||||||
"MediaDrmSettingsFlagsLib",
|
"MediaDrmSettingsFlagsLib",
|
||||||
"Settings-change-ids",
|
"Settings-change-ids",
|
||||||
"SettingsLib",
|
"SettingsLib",
|
||||||
"SettingsLibDataStore",
|
|
||||||
"SettingsLibActivityEmbedding",
|
"SettingsLibActivityEmbedding",
|
||||||
|
"SettingsLibDataStore",
|
||||||
|
"SettingsLibMetadata",
|
||||||
|
"SettingsLibPreference",
|
||||||
"aconfig_settings_flags_lib",
|
"aconfig_settings_flags_lib",
|
||||||
"accessibility_settings_flags_lib",
|
"accessibility_settings_flags_lib",
|
||||||
"contextualcards",
|
"contextualcards",
|
||||||
|
@@ -1297,7 +1297,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="Settings$ModesSettingsActivity"
|
android:name="Settings$ModesSettingsActivity"
|
||||||
android:label="@string/zen_modes_list_title"
|
android:label="@string/zen_modes_list_title"
|
||||||
android:icon="@drawable/ic_homepage_notification"
|
android:icon="@*android:drawable/ic_zen_priority_modes"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter android:priority="1"
|
<intent-filter android:priority="1"
|
||||||
android:featureFlag="android.app.modes_ui">
|
android:featureFlag="android.app.modes_ui">
|
||||||
@@ -2813,6 +2813,9 @@
|
|||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFinish" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFinish" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollParentalConsent" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollParentalConsent" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroduction"
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroduction"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/GlifTheme.Light" />
|
||||||
|
<activity android:name=".biometrics.fingerprint.FingerprintEnroll"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/GlifTheme.Light">
|
android:theme="@style/GlifTheme.Light">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -2823,9 +2826,13 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroductionInternal"
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroductionInternal"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/GlifTheme.Light"
|
android:theme="@style/GlifTheme.Light"
|
||||||
android:taskAffinity="com.android.settings.root" />
|
android:taskAffinity="com.android.settings.root" />
|
||||||
|
<activity android:name=".biometrics.fingerprint.FingerprintEnroll$InternalActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/GlifTheme.Light"
|
||||||
|
android:taskAffinity="com.android.settings.root" />
|
||||||
|
|
||||||
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollFindSensor"
|
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollFindSensor"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
@@ -2833,6 +2840,10 @@
|
|||||||
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollEnrolling" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollEnrolling" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollFinish" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollFinish" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollIntroduction"
|
<activity android:name=".biometrics.fingerprint.SetupFingerprintEnrollIntroduction"
|
||||||
|
android:exported="false"
|
||||||
|
android:permission="android.permission.MANAGE_FINGERPRINT"
|
||||||
|
android:theme="@style/GlifTheme.Light" />
|
||||||
|
<activity android:name=".biometrics.fingerprint.FingerprintEnroll$SetupActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.MANAGE_FINGERPRINT"
|
android:permission="android.permission.MANAGE_FINGERPRINT"
|
||||||
android:theme="@style/GlifTheme.Light">
|
android:theme="@style/GlifTheme.Light">
|
||||||
@@ -2842,7 +2853,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
<activity android:name=".biometrics.fingerprint2.ui.enrollment.activity.FingerprintEnrollmentV2Activity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.MANAGE_FINGERPRINT"
|
android:permission="android.permission.MANAGE_FINGERPRINT"
|
||||||
|
@@ -7867,7 +7867,9 @@
|
|||||||
<string name="keywords_keyboard_vibration">keyboard, haptics, vibrate,</string>
|
<string name="keywords_keyboard_vibration">keyboard, haptics, vibrate,</string>
|
||||||
|
|
||||||
<!-- Summary for sound settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
|
<!-- Summary for sound settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
|
||||||
<string name="sound_dashboard_summary">Volume, vibration, Do Not Disturb</string>
|
<string name="sound_dashboard_summary">Volume and vibration</string>
|
||||||
|
<!-- Summary for sound settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
|
||||||
|
<string name="sound_dashboard_summary_with_dnd">Volume, vibration, Do Not Disturb</string>
|
||||||
|
|
||||||
<!-- Sound: Title for the option managing media volume. [CHAR LIMIT=30] -->
|
<!-- Sound: Title for the option managing media volume. [CHAR LIMIT=30] -->
|
||||||
<string name="media_volume_option_title">Media volume</string>
|
<string name="media_volume_option_title">Media volume</string>
|
||||||
@@ -13366,9 +13368,9 @@
|
|||||||
<!-- Summary of the Live Caption enabled state. -->
|
<!-- Summary of the Live Caption enabled state. -->
|
||||||
<string name="live_caption_enabled">On</string>
|
<string name="live_caption_enabled">On</string>
|
||||||
<!-- State description for the Audio Balance seek bar, with left reported before right. -->
|
<!-- State description for the Audio Balance seek bar, with left reported before right. -->
|
||||||
<string name="audio_seek_bar_state_left_first">Audio %1$d%% left, %2$d%% right</string>
|
<string name="audio_seek_bar_state_left_first">Audio <xliff:g id="percent_left">%1$s</xliff:g> left, <xliff:g id="percent_right">%2$s</xliff:g> right</string>
|
||||||
<!-- State description for the Audio Balance seek bar, with right reported before left. -->
|
<!-- State description for the Audio Balance seek bar, with right reported before left. -->
|
||||||
<string name="audio_seek_bar_state_right_first">Audio %1$d%% right, %2$d%% left</string>
|
<string name="audio_seek_bar_state_right_first">Audio <xliff:g id="percent_right">%1$s</xliff:g> right, <xliff:g id="percent_left">%2$s</xliff:g> left</string>
|
||||||
|
|
||||||
<!-- Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
|
<!-- Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
|
||||||
<string name="about_phone_device_name_warning">Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
|
<string name="about_phone_device_name_warning">Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
|
||||||
@@ -13577,6 +13579,8 @@
|
|||||||
<string name="audio_streams_dialog_cannot_play">Can\u0027t play this audio stream on <xliff:g example="LE headset" id="device_name">%1$s</xliff:g>.</string>
|
<string name="audio_streams_dialog_cannot_play">Can\u0027t play this audio stream on <xliff:g example="LE headset" id="device_name">%1$s</xliff:g>.</string>
|
||||||
<!-- The preference summary when add source succeed [CHAR LIMIT=NONE] -->
|
<!-- The preference summary when add source succeed [CHAR LIMIT=NONE] -->
|
||||||
<string name="audio_streams_listening_now">Listening now</string>
|
<string name="audio_streams_listening_now">Listening now</string>
|
||||||
|
<!-- The preference summary when source is present on sinks [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="audio_streams_present_now">Paused by host</string>
|
||||||
<!-- Le audio streams service notification leave broadcast text [CHAR LIMIT=NONE] -->
|
<!-- Le audio streams service notification leave broadcast text [CHAR LIMIT=NONE] -->
|
||||||
<string name="audio_streams_media_service_notification_leave_broadcast_text">Stop listening</string>
|
<string name="audio_streams_media_service_notification_leave_broadcast_text">Stop listening</string>
|
||||||
<!-- Le audio streams no le device dialog title [CHAR LIMIT=NONE] -->
|
<!-- Le audio streams no le device dialog title [CHAR LIMIT=NONE] -->
|
||||||
|
@@ -104,8 +104,9 @@
|
|||||||
android:key="top_level_sound"
|
android:key="top_level_sound"
|
||||||
android:order="-90"
|
android:order="-90"
|
||||||
android:title="@string/sound_settings"
|
android:title="@string/sound_settings"
|
||||||
android:summary="@string/sound_dashboard_summary"
|
android:summary="@string/sound_dashboard_summary_with_dnd"
|
||||||
settings:highlightableMenuKey="@string/menu_key_sound"/>
|
settings:highlightableMenuKey="@string/menu_key_sound"
|
||||||
|
settings:controller="com.android.settings.sound.TopLevelSoundPreferenceController"/>
|
||||||
|
|
||||||
<com.android.settings.widget.HomepagePreference
|
<com.android.settings.widget.HomepagePreference
|
||||||
android:fragment="com.android.settings.DisplaySettings"
|
android:fragment="com.android.settings.DisplaySettings"
|
||||||
|
@@ -79,8 +79,9 @@
|
|||||||
android:key="top_level_sound"
|
android:key="top_level_sound"
|
||||||
android:order="-40"
|
android:order="-40"
|
||||||
android:title="@string/sound_settings"
|
android:title="@string/sound_settings"
|
||||||
android:summary="@string/sound_dashboard_summary"
|
android:summary="@string/sound_dashboard_summary_with_dnd"
|
||||||
settings:highlightableMenuKey="@string/menu_key_sound"/>
|
settings:highlightableMenuKey="@string/menu_key_sound"
|
||||||
|
settings:controller="com.android.settings.sound.TopLevelSoundPreferenceController"/>
|
||||||
|
|
||||||
<com.android.settings.widget.RestrictedHomepagePreference
|
<com.android.settings.widget.RestrictedHomepagePreference
|
||||||
android:fragment="com.android.settings.notification.modes.ZenModesListFragment"
|
android:fragment="com.android.settings.notification.modes.ZenModesListFragment"
|
||||||
|
@@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -101,6 +102,11 @@ public class AccessibilityActivityPreference extends RestrictedPreference {
|
|||||||
return mLabel;
|
return mLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ComponentName getComponentName() {
|
||||||
|
return mComponentName;
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable getA11yActivityIcon() {
|
private Drawable getA11yActivityIcon() {
|
||||||
ActivityInfo activityInfo = mA11yShortcutInfo.getActivityInfo();
|
ActivityInfo activityInfo = mA11yShortcutInfo.getActivityInfo();
|
||||||
Drawable serviceIcon;
|
Drawable serviceIcon;
|
||||||
|
@@ -16,8 +16,12 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.settingslib.search.SearchIndexableRaw;
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -28,10 +32,22 @@ import java.util.List;
|
|||||||
public interface AccessibilitySearchFeatureProvider {
|
public interface AccessibilitySearchFeatureProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of raw data for indexing. See {@link SearchIndexableRaw}
|
* Returns accessibility features to be searched where the accessibility features are always on
|
||||||
|
* the device and their feature names won't change.
|
||||||
*
|
*
|
||||||
* @param context a valid context {@link Context} instance
|
* @param context a valid context {@link Context} instance
|
||||||
* @return a list of {@link SearchIndexableRaw} references. Can be null.
|
* @return a list of {@link SearchIndexableRaw} references
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
List<SearchIndexableRaw> getSearchIndexableRawData(Context context);
|
List<SearchIndexableRaw> getSearchIndexableRawData(Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns synonyms of the Accessibility component that is used for search.
|
||||||
|
*
|
||||||
|
* @param context the context that is used for grabbing resources
|
||||||
|
* @param componentName the ComponentName of the accessibility feature
|
||||||
|
* @return a comma separated synonyms e.g. "wifi, wi-fi, network connection"
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String getSynonymsForComponent(@NonNull Context context, @NonNull ComponentName componentName);
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,12 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.settingslib.search.SearchIndexableRaw;
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,8 +31,16 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class AccessibilitySearchFeatureProviderImpl implements AccessibilitySearchFeatureProvider {
|
public class AccessibilitySearchFeatureProviderImpl implements AccessibilitySearchFeatureProvider {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public List<SearchIndexableRaw> getSearchIndexableRawData(Context context) {
|
public List<SearchIndexableRaw> getSearchIndexableRawData(Context context) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getSynonymsForComponent(@NonNull Context context,
|
||||||
|
@NonNull ComponentName componentName) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -95,6 +96,11 @@ public class AccessibilityServicePreference extends RestrictedPreference {
|
|||||||
super.performClick();
|
super.performClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ComponentName getComponentName() {
|
||||||
|
return mComponentName;
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable getA11yServiceIcon() {
|
private Drawable getA11yServiceIcon() {
|
||||||
ResolveInfo resolveInfo = mA11yServiceInfo.getResolveInfo();
|
ResolveInfo resolveInfo = mA11yServiceInfo.getResolveInfo();
|
||||||
Drawable serviceIcon;
|
Drawable serviceIcon;
|
||||||
|
@@ -473,7 +473,7 @@ public class AccessibilitySettings extends DashboardFragment implements
|
|||||||
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
||||||
* @param installedServiceList A list of installed {@link AccessibilityServiceInfo}s.
|
* @param installedServiceList A list of installed {@link AccessibilityServiceInfo}s.
|
||||||
*/
|
*/
|
||||||
private List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
|
private static List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
|
||||||
List<AccessibilityShortcutInfo> installedShortcutList,
|
List<AccessibilityShortcutInfo> installedShortcutList,
|
||||||
List<AccessibilityServiceInfo> installedServiceList) {
|
List<AccessibilityServiceInfo> installedServiceList) {
|
||||||
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
|
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
|
||||||
@@ -623,6 +623,51 @@ public class AccessibilitySettings extends DashboardFragment implements
|
|||||||
.getAccessibilitySearchFeatureProvider().getSearchIndexableRawData(
|
.getAccessibilitySearchFeatureProvider().getSearchIndexableRawData(
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context,
|
||||||
|
boolean enabled) {
|
||||||
|
List<SearchIndexableRaw> dynamicRawData = super.getDynamicRawDataToIndex(
|
||||||
|
context, enabled);
|
||||||
|
if (dynamicRawData == null) {
|
||||||
|
dynamicRawData = new ArrayList<>();
|
||||||
|
}
|
||||||
|
if (!Flags.fixA11ySettingsSearch()) {
|
||||||
|
return dynamicRawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessibilityManager a11yManager = context.getSystemService(
|
||||||
|
AccessibilityManager.class);
|
||||||
|
AccessibilitySearchFeatureProvider a11ySearchFeatureProvider =
|
||||||
|
FeatureFactory.getFeatureFactory()
|
||||||
|
.getAccessibilitySearchFeatureProvider();
|
||||||
|
List<RestrictedPreference> installedA11yFeaturesPref =
|
||||||
|
AccessibilitySettings.getInstalledAccessibilityPreferences(
|
||||||
|
context,
|
||||||
|
a11yManager.getInstalledAccessibilityShortcutListAsUser(
|
||||||
|
context, UserHandle.myUserId()),
|
||||||
|
a11yManager.getInstalledAccessibilityServiceList()
|
||||||
|
);
|
||||||
|
for (RestrictedPreference pref : installedA11yFeaturesPref) {
|
||||||
|
SearchIndexableRaw indexableRaw = new SearchIndexableRaw(context);
|
||||||
|
indexableRaw.key = pref.getKey();
|
||||||
|
indexableRaw.title = pref.getTitle().toString();
|
||||||
|
@NonNull String synonyms = "";
|
||||||
|
if (pref instanceof AccessibilityServicePreference) {
|
||||||
|
synonyms = a11ySearchFeatureProvider.getSynonymsForComponent(
|
||||||
|
context,
|
||||||
|
((AccessibilityServicePreference) pref).getComponentName());
|
||||||
|
} else if (pref instanceof AccessibilityActivityPreference) {
|
||||||
|
synonyms = a11ySearchFeatureProvider.getSynonymsForComponent(
|
||||||
|
context,
|
||||||
|
((AccessibilityActivityPreference) pref).getComponentName());
|
||||||
|
}
|
||||||
|
indexableRaw.keywords = synonyms;
|
||||||
|
dynamicRawData.add(indexableRaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dynamicRawData;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -36,6 +36,7 @@ import android.widget.SeekBar;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom seekbar for the balance setting.
|
* A custom seekbar for the balance setting.
|
||||||
@@ -178,10 +179,12 @@ public class BalanceSeekBar extends SeekBar {
|
|||||||
== LAYOUT_DIRECTION_RTL;
|
== LAYOUT_DIRECTION_RTL;
|
||||||
final int rightPercent = (int) (100 * (progress / max));
|
final int rightPercent = (int) (100 * (progress / max));
|
||||||
final int leftPercent = 100 - rightPercent;
|
final int leftPercent = 100 - rightPercent;
|
||||||
|
final String rightPercentString = Utils.formatPercentage(rightPercent);
|
||||||
|
final String leftPercentString = Utils.formatPercentage(leftPercent);
|
||||||
if (rightPercent > leftPercent || (rightPercent == leftPercent && isLayoutRtl)) {
|
if (rightPercent > leftPercent || (rightPercent == leftPercent && isLayoutRtl)) {
|
||||||
return context.getString(resIdRightFirst, rightPercent, leftPercent);
|
return context.getString(resIdRightFirst, rightPercentString, leftPercentString);
|
||||||
} else {
|
} else {
|
||||||
return context.getString(resIdLeftFirst, leftPercent, rightPercent);
|
return context.getString(resIdLeftFirst, leftPercentString, rightPercentString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,9 +40,8 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.SubSettings;
|
import com.android.settings.SubSettings;
|
||||||
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
||||||
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
|
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollActivityClassProvider;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
|
|
||||||
import com.android.settings.core.FeatureFlags;
|
import com.android.settings.core.FeatureFlags;
|
||||||
import com.android.settings.homepage.DeepLinkHomepageActivity;
|
import com.android.settings.homepage.DeepLinkHomepageActivity;
|
||||||
import com.android.settings.homepage.DeepLinkHomepageActivityInternal;
|
import com.android.settings.homepage.DeepLinkHomepageActivityInternal;
|
||||||
@@ -255,8 +254,12 @@ public class ActivityEmbeddingRulesController {
|
|||||||
.buildSearchIntent(mContext, SettingsEnums.SETTINGS_HOMEPAGE);
|
.buildSearchIntent(mContext, SettingsEnums.SETTINGS_HOMEPAGE);
|
||||||
addActivityFilter(activityFilters, searchIntent);
|
addActivityFilter(activityFilters, searchIntent);
|
||||||
}
|
}
|
||||||
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
|
final FingerprintEnrollActivityClassProvider fpClassProvider = FeatureFactory
|
||||||
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
|
.getFeatureFactory()
|
||||||
|
.getFingerprintFeatureProvider()
|
||||||
|
.getEnrollActivityClassProvider();
|
||||||
|
addActivityFilter(activityFilters, fpClassProvider.getDefault());
|
||||||
|
addActivityFilter(activityFilters, fpClassProvider.getInternal());
|
||||||
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
|
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
|
||||||
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
|
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
|
||||||
addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
|
addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
|
||||||
|
@@ -44,10 +44,9 @@ import com.android.internal.widget.VerifyCredentialResponse;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SetupWizardUtils;
|
import com.android.settings.SetupWizardUtils;
|
||||||
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintEnroll;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
|
||||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
|
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
|
||||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.password.ChooseLockGeneric;
|
import com.android.settings.password.ChooseLockGeneric;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
@@ -262,13 +261,13 @@ public class BiometricUtils {
|
|||||||
/**
|
/**
|
||||||
* @param context caller's context
|
* @param context caller's context
|
||||||
* @param activityIntent The intent that started the caller's activity
|
* @param activityIntent The intent that started the caller's activity
|
||||||
* @return Intent for starting FingerprintEnrollIntroduction
|
* @return Intent for starting FingerprintEnroll
|
||||||
*/
|
*/
|
||||||
public static Intent getFingerprintIntroIntent(@NonNull Context context,
|
public static Intent getFingerprintIntroIntent(@NonNull Context context,
|
||||||
@NonNull Intent activityIntent) {
|
@NonNull Intent activityIntent) {
|
||||||
final boolean isSuw = WizardManagerHelper.isAnySetupWizard(activityIntent);
|
final boolean isSuw = WizardManagerHelper.isAnySetupWizard(activityIntent);
|
||||||
final Intent intent = new Intent(context, isSuw
|
final Intent intent = new Intent(context, isSuw
|
||||||
? SetupFingerprintEnrollIntroduction.class : FingerprintEnrollIntroduction.class);
|
? FingerprintEnroll.SetupActivity.class : FingerprintEnroll.class);
|
||||||
if (isSuw) {
|
if (isSuw) {
|
||||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default class for handling fingerprint enrollment, designed to launch a subsequent activity and
|
||||||
|
* forward the result, then finish itself.
|
||||||
|
*/
|
||||||
|
open class FingerprintEnroll: AppCompatActivity() {
|
||||||
|
|
||||||
|
/** Inner class representing enrolling fingerprint enrollment in SetupWizard environment */
|
||||||
|
class SetupActivity : FingerprintEnroll() {
|
||||||
|
override val nextActivityClass: Class<*>
|
||||||
|
get() = enrollActivityProvider.setup
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Inner class representing enrolling fingerprint enrollment from FingerprintSettings */
|
||||||
|
class InternalActivity : FingerprintEnroll() {
|
||||||
|
override val nextActivityClass: Class<*>
|
||||||
|
get() = enrollActivityProvider.internal
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class of the next activity to launch. This is open to allow subclasses to provide their
|
||||||
|
* own behavior. Defaults to the default activity class provided by the
|
||||||
|
* enrollActivityClassProvider.
|
||||||
|
*/
|
||||||
|
open val nextActivityClass: Class<*>
|
||||||
|
get() = enrollActivityProvider.default
|
||||||
|
|
||||||
|
protected val enrollActivityProvider: FingerprintEnrollActivityClassProvider
|
||||||
|
get() = featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the next activity to be launched, creates an intent for that activity,
|
||||||
|
* adds flags to forward the result, includes any existing extras from the current intent,
|
||||||
|
* starts the new activity and then finishes the current one
|
||||||
|
*/
|
||||||
|
Log.d("FingerprintEnroll", "forward to $nextActivityClass")
|
||||||
|
val nextIntent = Intent(this, nextActivityClass)
|
||||||
|
nextIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
|
||||||
|
nextIntent.putExtras(intent)
|
||||||
|
startActivity(nextIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
|
||||||
|
open class FingerprintEnrollActivityClassProvider {
|
||||||
|
|
||||||
|
open val default: Class<out Activity>
|
||||||
|
get() = FingerprintEnrollIntroduction::class.java
|
||||||
|
open val setup: Class<out Activity>
|
||||||
|
get() = SetupFingerprintEnrollIntroduction::class.java
|
||||||
|
open val internal: Class<out Activity>
|
||||||
|
get() = FingerprintEnrollIntroductionInternal::class.java
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
val instance = FingerprintEnrollActivityClassProvider()
|
||||||
|
}
|
||||||
|
}
|
@@ -33,7 +33,6 @@ public interface FingerprintFeatureProvider {
|
|||||||
*/
|
*/
|
||||||
SfpsEnrollmentFeature getSfpsEnrollmentFeature();
|
SfpsEnrollmentFeature getSfpsEnrollmentFeature();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets calibrator for udfps pre-enroll
|
* Gets calibrator for udfps pre-enroll
|
||||||
* @param appContext application context
|
* @param appContext application context
|
||||||
@@ -52,4 +51,13 @@ public interface FingerprintFeatureProvider {
|
|||||||
* @return the feature implementation
|
* @return the feature implementation
|
||||||
*/
|
*/
|
||||||
SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context);
|
SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the provider for current fingerprint enrollment activity classes
|
||||||
|
* @return the provider
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
default FingerprintEnrollActivityClassProvider getEnrollActivityClassProvider() {
|
||||||
|
return FingerprintEnrollActivityClassProvider.getInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1142,7 +1142,7 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
private void addFirstFingerprint(@Nullable Long gkPwHandle) {
|
private void addFirstFingerprint(@Nullable Long gkPwHandle) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setClassName(SETTINGS_PACKAGE_NAME,
|
intent.setClassName(SETTINGS_PACKAGE_NAME,
|
||||||
FingerprintEnrollIntroductionInternal.class.getName());
|
FingerprintEnroll.InternalActivity.class.getName());
|
||||||
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
|
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
|
||||||
intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
|
intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
|
||||||
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
|
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
|
||||||
|
@@ -43,7 +43,7 @@ import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
|
|||||||
import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY
|
import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
import com.android.settings.biometrics.fingerprint.FingerprintEnroll.InternalActivity
|
||||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
|
||||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
|
||||||
@@ -514,7 +514,7 @@ class FingerprintSettingsV2Fragment :
|
|||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.setClassName(
|
intent.setClassName(
|
||||||
SETTINGS_PACKAGE_NAME,
|
SETTINGS_PACKAGE_NAME,
|
||||||
FingerprintEnrollIntroductionInternal::class.java.name,
|
InternalActivity::class.java.name,
|
||||||
)
|
)
|
||||||
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
|
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
|
||||||
intent.putExtra(
|
intent.putExtra(
|
||||||
|
@@ -22,4 +22,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
data class DeviceSettingLayout(val rows: List<DeviceSettingLayoutRow>)
|
data class DeviceSettingLayout(val rows: List<DeviceSettingLayoutRow>)
|
||||||
|
|
||||||
/** Represent a row in the layout. */
|
/** Represent a row in the layout. */
|
||||||
data class DeviceSettingLayoutRow(val settingIds: Flow<List<Int>>)
|
data class DeviceSettingLayoutRow(val columns: Flow<List<DeviceSettingLayoutColumn>>)
|
||||||
|
|
||||||
|
/** Represent a column in a row. */
|
||||||
|
data class DeviceSettingLayoutColumn(val settingId: Int, val highlighted: Boolean)
|
||||||
|
@@ -20,12 +20,23 @@ import android.bluetooth.BluetoothAdapter
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.expandVertically
|
||||||
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -43,7 +54,6 @@ import com.android.settings.core.SubSettingLauncher
|
|||||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||||
import com.android.settings.spa.preference.ComposePreference
|
import com.android.settings.spa.preference.ComposePreference
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||||
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
|
|
||||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
|
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
|
||||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
|
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
|
||||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||||
@@ -91,10 +101,16 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
) : DeviceDetailsFragmentFormatter {
|
) : DeviceDetailsFragmentFormatter {
|
||||||
private val repository =
|
private val repository =
|
||||||
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
|
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
|
||||||
context, bluetoothAdapter, fragment.lifecycleScope)
|
context,
|
||||||
|
bluetoothAdapter,
|
||||||
|
fragment.lifecycleScope,
|
||||||
|
)
|
||||||
private val spatialAudioInteractor =
|
private val spatialAudioInteractor =
|
||||||
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
|
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
|
||||||
context, context.getSystemService(AudioManager::class.java), fragment.lifecycleScope)
|
context,
|
||||||
|
context.getSystemService(AudioManager::class.java),
|
||||||
|
fragment.lifecycleScope,
|
||||||
|
)
|
||||||
private val viewModel: BluetoothDeviceDetailsViewModel =
|
private val viewModel: BluetoothDeviceDetailsViewModel =
|
||||||
ViewModelProvider(
|
ViewModelProvider(
|
||||||
fragment,
|
fragment,
|
||||||
@@ -104,7 +120,8 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
spatialAudioInteractor,
|
spatialAudioInteractor,
|
||||||
cachedDevice,
|
cachedDevice,
|
||||||
backgroundCoroutineContext,
|
backgroundCoroutineContext,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.get(BluetoothDeviceDetailsViewModel::class.java)
|
.get(BluetoothDeviceDetailsViewModel::class.java)
|
||||||
|
|
||||||
override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>? =
|
override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>? =
|
||||||
@@ -120,7 +137,8 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
viewModel
|
viewModel
|
||||||
.getItems(fragmentType)
|
.getItems(fragmentType)
|
||||||
?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem>()
|
?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem>()
|
||||||
?.first()?.invisibleProfiles
|
?.first()
|
||||||
|
?.invisibleProfiles
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates bluetooth device details fragment layout. */
|
/** Updates bluetooth device details fragment layout. */
|
||||||
@@ -144,7 +162,8 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
val settingId = items[row].settingId
|
val settingId = items[row].settingId
|
||||||
if (settingIdToXmlPreferences.containsKey(settingId)) {
|
if (settingIdToXmlPreferences.containsKey(settingId)) {
|
||||||
fragment.preferenceScreen.addPreference(
|
fragment.preferenceScreen.addPreference(
|
||||||
settingIdToXmlPreferences[settingId]!!.apply { order = row })
|
settingIdToXmlPreferences[settingId]!!.apply { order = row }
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val pref =
|
val pref =
|
||||||
ComposePreference(context)
|
ComposePreference(context)
|
||||||
@@ -169,7 +188,8 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
emitAll(
|
emitAll(
|
||||||
viewModel.getDeviceSetting(cachedDevice, item.settingId).map {
|
viewModel.getDeviceSetting(cachedDevice, item.settingId).map {
|
||||||
it as? DeviceSettingPreferenceModel.HelpPreference
|
it as? DeviceSettingPreferenceModel.HelpPreference
|
||||||
})
|
}
|
||||||
|
)
|
||||||
} ?: emit(null)
|
} ?: emit(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,22 +197,56 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
private fun buildPreference(layout: DeviceSettingLayout, row: Int) {
|
private fun buildPreference(layout: DeviceSettingLayout, row: Int) {
|
||||||
val contents by
|
val contents by
|
||||||
remember(row) {
|
remember(row) {
|
||||||
layout.rows[row].settingIds.flatMapLatest { settingIds ->
|
layout.rows[row].columns.flatMapLatest { columns ->
|
||||||
if (settingIds.isEmpty()) {
|
if (columns.isEmpty()) {
|
||||||
flowOf(emptyList<DeviceSettingPreferenceModel>())
|
flowOf(emptyList<DeviceSettingPreferenceModel>())
|
||||||
} else {
|
} else {
|
||||||
combine(
|
combine(
|
||||||
settingIds.map { settingId ->
|
columns.map { column ->
|
||||||
viewModel.getDeviceSetting(cachedDevice, settingId)
|
viewModel.getDeviceSetting(cachedDevice, column.settingId)
|
||||||
}) {
|
|
||||||
it.toList()
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
it.toList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.collectAsStateWithLifecycle(initialValue = listOf())
|
.collectAsStateWithLifecycle(initialValue = listOf())
|
||||||
|
|
||||||
|
val highlighted by
|
||||||
|
remember(row) {
|
||||||
|
layout.rows[row].columns.map { columns -> columns.any { it.highlighted } }
|
||||||
|
}
|
||||||
|
.collectAsStateWithLifecycle(initialValue = false)
|
||||||
|
|
||||||
val settings = contents
|
val settings = contents
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = settings.isNotEmpty(),
|
||||||
|
enter = expandVertically(expandFrom = Alignment.Top),
|
||||||
|
exit = shrinkVertically(shrinkTowards = Alignment.Top),
|
||||||
|
) {
|
||||||
|
Box {
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
Modifier.matchParentSize()
|
||||||
|
.padding(16.dp, 0.dp, 8.dp, 0.dp)
|
||||||
|
.background(
|
||||||
|
color =
|
||||||
|
if (highlighted) {
|
||||||
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
Color.Transparent
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(28.dp),
|
||||||
|
),
|
||||||
|
) {}
|
||||||
|
buildPreferences(settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun buildPreferences(settings: List<DeviceSettingPreferenceModel?>) {
|
||||||
when (settings.size) {
|
when (settings.size) {
|
||||||
0 -> {}
|
0 -> {}
|
||||||
1 -> {
|
1 -> {
|
||||||
@@ -217,11 +271,18 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (!settings.all { it is DeviceSettingPreferenceModel.MultiTogglePreference }) {
|
if (
|
||||||
|
!settings.all {
|
||||||
|
it is DeviceSettingPreferenceModel.MultiTogglePreference
|
||||||
|
}
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buildMultiTogglePreference(
|
buildMultiTogglePreference(
|
||||||
settings.filterIsInstance<DeviceSettingPreferenceModel.MultiTogglePreference>())
|
settings.filterIsInstance<
|
||||||
|
DeviceSettingPreferenceModel.MultiTogglePreference
|
||||||
|
>()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,11 +304,19 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
override val onCheckedChange = { newChecked: Boolean ->
|
override val onCheckedChange = { newChecked: Boolean ->
|
||||||
model.onCheckedChange(newChecked)
|
model.onCheckedChange(newChecked)
|
||||||
}
|
}
|
||||||
override val icon = @Composable { deviceSettingIcon(model.icon) }
|
override val icon: (@Composable () -> Unit)?
|
||||||
|
get() {
|
||||||
|
if (model.icon == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return { deviceSettingIcon(model.icon) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (model.onPrimaryClick != null) {
|
if (model.onPrimaryClick != null) {
|
||||||
TwoTargetSwitchPreference(
|
TwoTargetSwitchPreference(
|
||||||
switchPrefModel, primaryOnClick = model.onPrimaryClick::invoke)
|
switchPrefModel,
|
||||||
|
primaryOnClick = model.onPrimaryClick::invoke,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
SwitchPreference(switchPrefModel)
|
SwitchPreference(switchPrefModel)
|
||||||
}
|
}
|
||||||
@@ -263,8 +332,15 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
model.onClick?.invoke()
|
model.onClick?.invoke()
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
override val icon = @Composable { deviceSettingIcon(model.icon) }
|
override val icon: (@Composable () -> Unit)?
|
||||||
})
|
get() {
|
||||||
|
if (model.icon == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return { deviceSettingIcon(model.icon) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -281,11 +357,13 @@ class DeviceDetailsFragmentFormatterImpl(
|
|||||||
.setDestination(DeviceDetailsMoreSettingsFragment::class.java.name)
|
.setDestination(DeviceDetailsMoreSettingsFragment::class.java.name)
|
||||||
.setSourceMetricsCategory(fragment.getMetricsCategory())
|
.setSourceMetricsCategory(fragment.getMetricsCategory())
|
||||||
.setArguments(
|
.setArguments(
|
||||||
Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) })
|
Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) }
|
||||||
|
)
|
||||||
.launch()
|
.launch()
|
||||||
}
|
}
|
||||||
override val icon = @Composable { deviceSettingIcon(null) }
|
override val icon = @Composable { deviceSettingIcon(null) }
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@@ -24,6 +24,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
|
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
|
||||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
|
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
|
||||||
|
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutColumn
|
||||||
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
|
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
|
||||||
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
|
||||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
|
||||||
@@ -36,7 +37,6 @@ import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSetti
|
|||||||
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
|
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -51,7 +51,7 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
private val spatialAudioInteractor: SpatialAudioInteractor,
|
private val spatialAudioInteractor: SpatialAudioInteractor,
|
||||||
private val cachedDevice: CachedBluetoothDevice,
|
private val cachedDevice: CachedBluetoothDevice,
|
||||||
backgroundCoroutineContext: CoroutineContext,
|
backgroundCoroutineContext: CoroutineContext,
|
||||||
) : AndroidViewModel(application){
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
private val items =
|
private val items =
|
||||||
viewModelScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
|
viewModelScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
|
||||||
@@ -74,7 +74,7 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
|
|
||||||
fun getDeviceSetting(
|
fun getDeviceSetting(
|
||||||
cachedDevice: CachedBluetoothDevice,
|
cachedDevice: CachedBluetoothDevice,
|
||||||
@DeviceSettingId settingId: Int
|
@DeviceSettingId settingId: Int,
|
||||||
): Flow<DeviceSettingPreferenceModel?> {
|
): Flow<DeviceSettingPreferenceModel?> {
|
||||||
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) {
|
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) {
|
||||||
return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId))
|
return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId))
|
||||||
@@ -98,16 +98,19 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
checked = switchState?.checked ?: false,
|
checked = switchState?.checked ?: false,
|
||||||
onCheckedChange = { newState ->
|
onCheckedChange = { newState ->
|
||||||
updateState?.invoke(
|
updateState?.invoke(
|
||||||
DeviceSettingStateModel.ActionSwitchPreferenceState(newState))
|
DeviceSettingStateModel.ActionSwitchPreferenceState(newState)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onPrimaryClick = { intent?.let { application.startActivity(it) } })
|
onPrimaryClick = { intent?.let { application.startActivity(it) } },
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
DeviceSettingPreferenceModel.PlainPreference(
|
DeviceSettingPreferenceModel.PlainPreference(
|
||||||
id = id,
|
id = id,
|
||||||
title = title,
|
title = title,
|
||||||
summary = summary,
|
summary = summary,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
onClick = { intent?.let { application.startActivity(it) } })
|
onClick = { intent?.let { application.startActivity(it) } },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is DeviceSettingModel.FooterPreference ->
|
is DeviceSettingModel.FooterPreference ->
|
||||||
@@ -116,9 +119,8 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
DeviceSettingPreferenceModel.HelpPreference(
|
DeviceSettingPreferenceModel.HelpPreference(
|
||||||
id = id,
|
id = id,
|
||||||
icon = DeviceSettingIcon.ResourceIcon(R.drawable.ic_help),
|
icon = DeviceSettingIcon.ResourceIcon(R.drawable.ic_help),
|
||||||
onClick = {
|
onClick = { application.startActivity(intent) },
|
||||||
application.startActivity(intent)
|
)
|
||||||
})
|
|
||||||
is DeviceSettingModel.MultiTogglePreference ->
|
is DeviceSettingModel.MultiTogglePreference ->
|
||||||
DeviceSettingPreferenceModel.MultiTogglePreference(
|
DeviceSettingPreferenceModel.MultiTogglePreference(
|
||||||
id = id,
|
id = id,
|
||||||
@@ -129,7 +131,8 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
isAllowedChangingState = isAllowedChangingState,
|
isAllowedChangingState = isAllowedChangingState,
|
||||||
onSelectedChange = { newState ->
|
onSelectedChange = { newState ->
|
||||||
updateState(DeviceSettingStateModel.MultiTogglePreferenceState(newState))
|
updateState(DeviceSettingStateModel.MultiTogglePreferenceState(newState))
|
||||||
})
|
},
|
||||||
|
)
|
||||||
is DeviceSettingModel.Unknown -> null
|
is DeviceSettingModel.Unknown -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,8 +148,8 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
configItems.map { idToDeviceSetting[it.settingId] ?: flowOf(null) }
|
configItems.map { idToDeviceSetting[it.settingId] ?: flowOf(null) }
|
||||||
val positionToSettingIds =
|
val positionToSettingIds =
|
||||||
combine(configDeviceSetting) { settings ->
|
combine(configDeviceSetting) { settings ->
|
||||||
val positionMapping = mutableMapOf<Int, List<Int>>()
|
val positionMapping = mutableMapOf<Int, List<DeviceSettingLayoutColumn>>()
|
||||||
var multiToggleSettingIds: MutableList<Int>? = null
|
var multiToggleSettingIds: MutableList<DeviceSettingLayoutColumn>? = null
|
||||||
for (i in settings.indices) {
|
for (i in settings.indices) {
|
||||||
val configItem = configItems[i]
|
val configItem = configItems[i]
|
||||||
val setting = settings[i]
|
val setting = settings[i]
|
||||||
@@ -156,14 +159,31 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
}
|
}
|
||||||
if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) {
|
if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) {
|
||||||
multiToggleSettingIds = null
|
multiToggleSettingIds = null
|
||||||
positionMapping[i] = listOf(configItem.settingId)
|
positionMapping[i] =
|
||||||
|
listOf(
|
||||||
|
DeviceSettingLayoutColumn(
|
||||||
|
configItem.settingId,
|
||||||
|
configItem.highlighted,
|
||||||
|
)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiToggleSettingIds != null) {
|
if (multiToggleSettingIds != null) {
|
||||||
multiToggleSettingIds.add(setting.id)
|
multiToggleSettingIds.add(
|
||||||
|
DeviceSettingLayoutColumn(
|
||||||
|
configItem.settingId,
|
||||||
|
configItem.highlighted,
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
multiToggleSettingIds = mutableListOf(setting.id)
|
multiToggleSettingIds =
|
||||||
|
mutableListOf(
|
||||||
|
DeviceSettingLayoutColumn(
|
||||||
|
configItem.settingId,
|
||||||
|
configItem.highlighted,
|
||||||
|
)
|
||||||
|
)
|
||||||
positionMapping[i] = multiToggleSettingIds
|
positionMapping[i] = multiToggleSettingIds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +193,8 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
return DeviceSettingLayout(
|
return DeviceSettingLayout(
|
||||||
configItems.indices.map { idx ->
|
configItems.indices.map { idx ->
|
||||||
DeviceSettingLayoutRow(positionToSettingIds.map { it[idx] ?: emptyList() })
|
DeviceSettingLayoutRow(positionToSettingIds.map { it[idx] ?: emptyList() })
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(
|
class Factory(
|
||||||
@@ -186,9 +207,12 @@ class BluetoothDeviceDetailsViewModel(
|
|||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return BluetoothDeviceDetailsViewModel(
|
return BluetoothDeviceDetailsViewModel(
|
||||||
application, deviceSettingRepository, spatialAudioInteractor,
|
application,
|
||||||
|
deviceSettingRepository,
|
||||||
|
spatialAudioInteractor,
|
||||||
cachedDevice,
|
cachedDevice,
|
||||||
backgroundCoroutineContext)
|
backgroundCoroutineContext,
|
||||||
|
)
|
||||||
as T
|
as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
||||||
@@ -41,6 +43,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
import com.android.settingslib.widget.ActionButtonsPreference;
|
import com.android.settingslib.widget.ActionButtonsPreference;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@@ -73,12 +76,18 @@ public class AudioStreamButtonController extends BasePreferenceController
|
|||||||
int sourceId,
|
int sourceId,
|
||||||
BluetoothLeBroadcastReceiveState state) {
|
BluetoothLeBroadcastReceiveState state) {
|
||||||
super.onReceiveStateChanged(sink, sourceId, state);
|
super.onReceiveStateChanged(sink, sourceId, state);
|
||||||
if (AudioStreamsHelper.isConnected(state)) {
|
boolean shouldUpdateButton =
|
||||||
|
audioSharingHysteresisModeFix()
|
||||||
|
? AudioStreamsHelper.hasSourcePresent(state)
|
||||||
|
: AudioStreamsHelper.isConnected(state);
|
||||||
|
if (shouldUpdateButton) {
|
||||||
updateButton();
|
updateButton();
|
||||||
mMetricsFeatureProvider.action(
|
if (AudioStreamsHelper.isConnected(state)) {
|
||||||
mContext,
|
mMetricsFeatureProvider.action(
|
||||||
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
|
mContext,
|
||||||
SOURCE_ORIGIN_REPOSITORY);
|
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
|
||||||
|
SOURCE_ORIGIN_REPOSITORY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +155,13 @@ public class AudioStreamButtonController extends BasePreferenceController
|
|||||||
Log.w(TAG, "updateButton(): preference is null!");
|
Log.w(TAG, "updateButton(): preference is null!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<BluetoothLeBroadcastReceiveState> sources =
|
||||||
|
audioSharingHysteresisModeFix()
|
||||||
|
? mAudioStreamsHelper.getAllPresentSources()
|
||||||
|
: mAudioStreamsHelper.getAllConnectedSources();
|
||||||
boolean isConnected =
|
boolean isConnected =
|
||||||
mAudioStreamsHelper.getAllConnectedSources().stream()
|
sources.stream()
|
||||||
.map(BluetoothLeBroadcastReceiveState::getBroadcastId)
|
.map(BluetoothLeBroadcastReceiveState::getBroadcastId)
|
||||||
.anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId);
|
.anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId);
|
||||||
|
|
||||||
|
@@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
@@ -48,6 +52,8 @@ public class AudioStreamHeaderController extends BasePreferenceController
|
|||||||
static final int AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY =
|
static final int AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY =
|
||||||
R.string.audio_streams_listening_now;
|
R.string.audio_streams_listening_now;
|
||||||
|
|
||||||
|
static final int AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY = R.string.audio_streams_present_now;
|
||||||
|
|
||||||
@VisibleForTesting static final String AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY = "";
|
@VisibleForTesting static final String AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY = "";
|
||||||
private static final String TAG = "AudioStreamHeaderController";
|
private static final String TAG = "AudioStreamHeaderController";
|
||||||
private static final String KEY = "audio_stream_header";
|
private static final String KEY = "audio_stream_header";
|
||||||
@@ -80,6 +86,10 @@ public class AudioStreamHeaderController extends BasePreferenceController
|
|||||||
updateSummary();
|
updateSummary();
|
||||||
mAudioStreamsHelper.startMediaService(
|
mAudioStreamsHelper.startMediaService(
|
||||||
mContext, mBroadcastId, mBroadcastName);
|
mContext, mBroadcastId, mBroadcastName);
|
||||||
|
} else if (audioSharingHysteresisModeFix()
|
||||||
|
&& AudioStreamsHelper.hasSourcePresent(state)) {
|
||||||
|
// if source present but not connected, only update the summary
|
||||||
|
updateSummary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -140,8 +150,27 @@ public class AudioStreamHeaderController extends BasePreferenceController
|
|||||||
var unused =
|
var unused =
|
||||||
ThreadUtils.postOnBackgroundThread(
|
ThreadUtils.postOnBackgroundThread(
|
||||||
() -> {
|
() -> {
|
||||||
|
var connectedSourceList =
|
||||||
|
mAudioStreamsHelper.getAllPresentSources().stream()
|
||||||
|
.filter(
|
||||||
|
state ->
|
||||||
|
(state.getBroadcastId()
|
||||||
|
== mBroadcastId))
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
var latestSummary =
|
var latestSummary =
|
||||||
mAudioStreamsHelper.getAllConnectedSources().stream()
|
audioSharingHysteresisModeFix()
|
||||||
|
? connectedSourceList.isEmpty()
|
||||||
|
? AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY
|
||||||
|
: (connectedSourceList.stream()
|
||||||
|
.anyMatch(
|
||||||
|
AudioStreamsHelper
|
||||||
|
::isConnected)
|
||||||
|
? mContext.getString(
|
||||||
|
AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY)
|
||||||
|
: mContext.getString(
|
||||||
|
AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY))
|
||||||
|
: mAudioStreamsHelper.getAllConnectedSources().stream()
|
||||||
.map(
|
.map(
|
||||||
BluetoothLeBroadcastReceiveState
|
BluetoothLeBroadcastReceiveState
|
||||||
::getBroadcastId)
|
::getBroadcastId)
|
||||||
@@ -149,9 +178,10 @@ public class AudioStreamHeaderController extends BasePreferenceController
|
|||||||
connectedBroadcastId ->
|
connectedBroadcastId ->
|
||||||
connectedBroadcastId
|
connectedBroadcastId
|
||||||
== mBroadcastId)
|
== mBroadcastId)
|
||||||
? mContext.getString(
|
? mContext.getString(
|
||||||
AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY)
|
AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY)
|
||||||
: AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
|
: AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
|
||||||
|
|
||||||
ThreadUtils.postOnMainThread(
|
ThreadUtils.postOnMainThread(
|
||||||
() -> {
|
() -> {
|
||||||
if (mHeaderController != null) {
|
if (mHeaderController != null) {
|
||||||
|
@@ -18,6 +18,8 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
|||||||
|
|
||||||
import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
|
import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
@@ -94,8 +96,12 @@ class AudioStreamStateHandler {
|
|||||||
}
|
}
|
||||||
preference.setIsConnected(
|
preference.setIsConnected(
|
||||||
newState
|
newState
|
||||||
== AudioStreamsProgressCategoryController.AudioStreamState
|
== AudioStreamsProgressCategoryController
|
||||||
.SOURCE_ADDED);
|
.AudioStreamState.SOURCE_ADDED
|
||||||
|
|| (audioSharingHysteresisModeFix()
|
||||||
|
&& newState
|
||||||
|
== AudioStreamsProgressCategoryController
|
||||||
|
.AudioStreamState.SOURCE_PRESENT));
|
||||||
preference.setOnPreferenceClickListener(getOnClickListener(controller));
|
preference.setOnPreferenceClickListener(getOnClickListener(controller));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
|||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_TITLE;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_TITLE;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
@@ -63,6 +64,12 @@ public class AudioStreamsHelper {
|
|||||||
|
|
||||||
private final @Nullable LocalBluetoothManager mBluetoothManager;
|
private final @Nullable LocalBluetoothManager mBluetoothManager;
|
||||||
private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||||
|
// Referring to Broadcast Audio Scan Service 1.0
|
||||||
|
// Table 3.9: Broadcast Receive State characteristic format
|
||||||
|
// 0x00000000: 0b0 = Not synchronized to BIS_index[x]
|
||||||
|
// 0xFFFFFFFF: Failed to sync to BIG
|
||||||
|
private static final long BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
|
||||||
|
private static final long BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
|
||||||
|
|
||||||
AudioStreamsHelper(@Nullable LocalBluetoothManager bluetoothManager) {
|
AudioStreamsHelper(@Nullable LocalBluetoothManager bluetoothManager) {
|
||||||
mBluetoothManager = bluetoothManager;
|
mBluetoothManager = bluetoothManager;
|
||||||
@@ -144,6 +151,19 @@ public class AudioStreamsHelper {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Retrieves a list of all LE broadcast receive states from sinks with source present. */
|
||||||
|
@VisibleForTesting
|
||||||
|
public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
|
||||||
|
if (mLeBroadcastAssistant == null) {
|
||||||
|
Log.w(TAG, "getAllPresentSources(): LeBroadcastAssistant is null!");
|
||||||
|
return emptyList();
|
||||||
|
}
|
||||||
|
return getConnectedBluetoothDevices(mBluetoothManager, /* inSharingOnly= */ true).stream()
|
||||||
|
.flatMap(sink -> mLeBroadcastAssistant.getAllSources(sink).stream())
|
||||||
|
.filter(AudioStreamsHelper::hasSourcePresent)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
/** Retrieves LocalBluetoothLeBroadcastAssistant. */
|
/** Retrieves LocalBluetoothLeBroadcastAssistant. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -153,7 +173,18 @@ public class AudioStreamsHelper {
|
|||||||
|
|
||||||
/** Checks the connectivity status based on the provided broadcast receive state. */
|
/** Checks the connectivity status based on the provided broadcast receive state. */
|
||||||
public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
|
public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
|
||||||
return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0);
|
return state.getBisSyncState().stream()
|
||||||
|
.anyMatch(
|
||||||
|
bitmap ->
|
||||||
|
(bitmap != BIS_SYNC_NOT_SYNC_TO_BIS
|
||||||
|
&& bitmap != BIS_SYNC_FAILED_SYNC_TO_BIG));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks the connectivity status based on the provided broadcast receive state. */
|
||||||
|
public static boolean hasSourcePresent(BluetoothLeBroadcastReceiveState state) {
|
||||||
|
// Referring to Broadcast Audio Scan Service 1.0
|
||||||
|
// All zero address means no source on the sink device
|
||||||
|
return !state.getSourceDevice().getAddress().equals("00:00:00:00:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isBadCode(BluetoothLeBroadcastReceiveState state) {
|
static boolean isBadCode(BluetoothLeBroadcastReceiveState state) {
|
||||||
@@ -242,7 +273,8 @@ public class AudioStreamsHelper {
|
|||||||
List<BluetoothLeBroadcastReceiveState> sourceList =
|
List<BluetoothLeBroadcastReceiveState> sourceList =
|
||||||
assistant.getAllSources(cachedDevice.getDevice());
|
assistant.getAllSources(cachedDevice.getDevice());
|
||||||
if (!sourceList.isEmpty()
|
if (!sourceList.isEmpty()
|
||||||
&& sourceList.stream().anyMatch(AudioStreamsHelper::isConnected)) {
|
&& (audioSharingHysteresisModeFix()
|
||||||
|
|| sourceList.stream().anyMatch(AudioStreamsHelper::isConnected))) {
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"Lead device has connected broadcast source, device = "
|
"Lead device has connected broadcast source, device = "
|
||||||
@@ -253,7 +285,9 @@ public class AudioStreamsHelper {
|
|||||||
for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
|
for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
|
||||||
List<BluetoothLeBroadcastReceiveState> list =
|
List<BluetoothLeBroadcastReceiveState> list =
|
||||||
assistant.getAllSources(device.getDevice());
|
assistant.getAllSources(device.getDevice());
|
||||||
if (!list.isEmpty() && list.stream().anyMatch(AudioStreamsHelper::isConnected)) {
|
if (!list.isEmpty()
|
||||||
|
&& (audioSharingHysteresisModeFix()
|
||||||
|
|| list.stream().anyMatch(AudioStreamsHelper::isConnected))) {
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"Member device has connected broadcast source, device = "
|
"Member device has connected broadcast source, device = "
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
@@ -39,6 +41,9 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
|
|||||||
mCategoryController.handleSourceConnected(state);
|
mCategoryController.handleSourceConnected(state);
|
||||||
} else if (AudioStreamsHelper.isBadCode(state)) {
|
} else if (AudioStreamsHelper.isBadCode(state)) {
|
||||||
mCategoryController.handleSourceConnectBadCode(state);
|
mCategoryController.handleSourceConnectBadCode(state);
|
||||||
|
} else if (audioSharingHysteresisModeFix() && AudioStreamsHelper.hasSourcePresent(state)) {
|
||||||
|
// Keep this check as the last, source might also present in above states
|
||||||
|
mCategoryController.handleSourcePresent(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
@@ -48,6 +50,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
|||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -95,9 +98,14 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
private final Comparator<AudioStreamPreference> mComparator =
|
private final Comparator<AudioStreamPreference> mComparator =
|
||||||
Comparator.<AudioStreamPreference, Boolean>comparing(
|
Comparator.<AudioStreamPreference, Boolean>comparing(
|
||||||
p ->
|
p ->
|
||||||
p.getAudioStreamState()
|
(p.getAudioStreamState()
|
||||||
== AudioStreamsProgressCategoryController
|
== AudioStreamsProgressCategoryController
|
||||||
.AudioStreamState.SOURCE_ADDED)
|
.AudioStreamState.SOURCE_ADDED
|
||||||
|
|| (audioSharingHysteresisModeFix()
|
||||||
|
&& p.getAudioStreamState()
|
||||||
|
== AudioStreamsProgressCategoryController
|
||||||
|
.AudioStreamState
|
||||||
|
.SOURCE_PRESENT)))
|
||||||
.thenComparingInt(AudioStreamPreference::getAudioStreamRssi)
|
.thenComparingInt(AudioStreamPreference::getAudioStreamRssi)
|
||||||
.reversed();
|
.reversed();
|
||||||
|
|
||||||
@@ -113,6 +121,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
ADD_SOURCE_BAD_CODE,
|
ADD_SOURCE_BAD_CODE,
|
||||||
// When addSource result in other bad state.
|
// When addSource result in other bad state.
|
||||||
ADD_SOURCE_FAILED,
|
ADD_SOURCE_FAILED,
|
||||||
|
// Source is present on sink.
|
||||||
|
SOURCE_PRESENT,
|
||||||
// Source is added to active sink.
|
// Source is added to active sink.
|
||||||
SOURCE_ADDED,
|
SOURCE_ADDED,
|
||||||
}
|
}
|
||||||
@@ -243,10 +253,13 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
existingPreference, AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
|
existingPreference, AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
|
||||||
} else {
|
} else {
|
||||||
// A preference with source founded existed either because it's already
|
// A preference with source founded existed either because it's already
|
||||||
// connected (SOURCE_ADDED). Any other reason is unexpected. We update the
|
// connected (SOURCE_ADDED) or present (SOURCE_PRESENT). Any other reason
|
||||||
// preference with this source and won't change it's state.
|
// is unexpected. We update the preference with this source and won't
|
||||||
|
// change it's state.
|
||||||
existingPreference.setAudioStreamMetadata(source);
|
existingPreference.setAudioStreamMetadata(source);
|
||||||
if (fromState != AudioStreamState.SOURCE_ADDED) {
|
if (fromState != AudioStreamState.SOURCE_ADDED
|
||||||
|
&& (!audioSharingHysteresisModeFix()
|
||||||
|
|| fromState != AudioStreamState.SOURCE_PRESENT)) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
"handleSourceFound(): unexpected state : "
|
"handleSourceFound(): unexpected state : "
|
||||||
@@ -346,10 +359,14 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
for (var entry : mBroadcastIdToPreferenceMap.entrySet()) {
|
for (var entry : mBroadcastIdToPreferenceMap.entrySet()) {
|
||||||
var preference = entry.getValue();
|
var preference = entry.getValue();
|
||||||
|
|
||||||
// Look for preference has SOURCE_ADDED state, re-check if they are still connected. If
|
// Look for preference has SOURCE_ADDED or SOURCE_PRESENT state, re-check if they are
|
||||||
|
// still connected. If
|
||||||
// not, means the source is removed from the sink, we move back the preference to SYNCED
|
// not, means the source is removed from the sink, we move back the preference to SYNCED
|
||||||
// state.
|
// state.
|
||||||
if (preference.getAudioStreamState() == AudioStreamState.SOURCE_ADDED
|
if ((preference.getAudioStreamState() == AudioStreamState.SOURCE_ADDED
|
||||||
|
|| (audioSharingHysteresisModeFix()
|
||||||
|
&& preference.getAudioStreamState()
|
||||||
|
== AudioStreamState.SOURCE_PRESENT))
|
||||||
&& mAudioStreamsHelper.getAllConnectedSources().stream()
|
&& mAudioStreamsHelper.getAllConnectedSources().stream()
|
||||||
.noneMatch(
|
.noneMatch(
|
||||||
connected ->
|
connected ->
|
||||||
@@ -383,6 +400,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
if (!AudioStreamsHelper.isConnected(receiveState)) {
|
if (!AudioStreamsHelper.isConnected(receiveState)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var broadcastIdConnected = receiveState.getBroadcastId();
|
var broadcastIdConnected = receiveState.getBroadcastId();
|
||||||
if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
|
if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
|
||||||
// mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
|
// mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
|
||||||
@@ -455,6 +473,58 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find preference by receiveState and decide next state.
|
||||||
|
// Expect one preference existed, move to SOURCE_PRESENT
|
||||||
|
void handleSourcePresent(BluetoothLeBroadcastReceiveState receiveState) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "handleSourcePresent()");
|
||||||
|
}
|
||||||
|
if (!AudioStreamsHelper.hasSourcePresent(receiveState)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var broadcastIdConnected = receiveState.getBroadcastId();
|
||||||
|
if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
|
||||||
|
// mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
|
||||||
|
// connected source receiveState.
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"handleSourcePresent() : processing mSourceFromQrCode with broadcastId"
|
||||||
|
+ " unset");
|
||||||
|
}
|
||||||
|
boolean updated =
|
||||||
|
maybeUpdateId(
|
||||||
|
AudioStreamsHelper.getBroadcastName(receiveState),
|
||||||
|
receiveState.getBroadcastId());
|
||||||
|
if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
|
||||||
|
var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
|
||||||
|
mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mBroadcastIdToPreferenceMap.compute(
|
||||||
|
broadcastIdConnected,
|
||||||
|
(k, existingPreference) -> {
|
||||||
|
if (existingPreference == null) {
|
||||||
|
// No existing preference for this source even if it's already connected,
|
||||||
|
// add one and set initial state to SOURCE_PRESENT. This could happen
|
||||||
|
// because
|
||||||
|
// we retrieves the connected source during onStart() from
|
||||||
|
// AudioStreamsHelper#getAllPresentSources() even before the source is
|
||||||
|
// founded by scanning.
|
||||||
|
return addNewPreference(receiveState, AudioStreamState.SOURCE_PRESENT);
|
||||||
|
}
|
||||||
|
if (existingPreference.getAudioStreamState() == AudioStreamState.WAIT_FOR_SYNC
|
||||||
|
&& existingPreference.getAudioStreamBroadcastId() == UNSET_BROADCAST_ID
|
||||||
|
&& mSourceFromQrCode != null) {
|
||||||
|
existingPreference.setAudioStreamMetadata(mSourceFromQrCode);
|
||||||
|
}
|
||||||
|
moveToState(existingPreference, AudioStreamState.SOURCE_PRESENT);
|
||||||
|
return existingPreference;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Find preference by metadata and decide next state.
|
// Find preference by metadata and decide next state.
|
||||||
// Expect one preference existed, move to ADD_SOURCE_WAIT_FOR_RESPONSE
|
// Expect one preference existed, move to ADD_SOURCE_WAIT_FOR_RESPONSE
|
||||||
void handleSourceAddRequest(
|
void handleSourceAddRequest(
|
||||||
@@ -530,9 +600,23 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
// Handle QR code scan, display currently connected streams then start scanning
|
// Handle QR code scan, display currently connected streams then start scanning
|
||||||
// sequentially
|
// sequentially
|
||||||
handleSourceFromQrCodeIfExists();
|
handleSourceFromQrCodeIfExists();
|
||||||
mAudioStreamsHelper
|
if (audioSharingHysteresisModeFix()) {
|
||||||
.getAllConnectedSources()
|
// With hysteresis mode, we prioritize showing connected sources first.
|
||||||
.forEach(this::handleSourceConnected);
|
// If no connected sources are found, we then show present sources.
|
||||||
|
List<BluetoothLeBroadcastReceiveState> sources =
|
||||||
|
mAudioStreamsHelper.getAllConnectedSources();
|
||||||
|
if (!sources.isEmpty()) {
|
||||||
|
sources.forEach(this::handleSourceConnected);
|
||||||
|
} else {
|
||||||
|
mAudioStreamsHelper
|
||||||
|
.getAllPresentSources()
|
||||||
|
.forEach(this::handleSourcePresent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mAudioStreamsHelper
|
||||||
|
.getAllConnectedSources()
|
||||||
|
.forEach(this::handleSourceConnected);
|
||||||
|
}
|
||||||
mLeBroadcastAssistant.startSearchingForSources(emptyList());
|
mLeBroadcastAssistant.startSearchingForSources(emptyList());
|
||||||
mMediaControlHelper.start();
|
mMediaControlHelper.start();
|
||||||
});
|
});
|
||||||
@@ -581,6 +665,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
AddSourceWaitForResponseState.getInstance();
|
AddSourceWaitForResponseState.getInstance();
|
||||||
case ADD_SOURCE_BAD_CODE -> AddSourceBadCodeState.getInstance();
|
case ADD_SOURCE_BAD_CODE -> AddSourceBadCodeState.getInstance();
|
||||||
case ADD_SOURCE_FAILED -> AddSourceFailedState.getInstance();
|
case ADD_SOURCE_FAILED -> AddSourceFailedState.getInstance();
|
||||||
|
case SOURCE_PRESENT -> SourcePresentState.getInstance();
|
||||||
case SOURCE_ADDED -> SourceAddedState.getInstance();
|
case SOURCE_ADDED -> SourceAddedState.getInstance();
|
||||||
default -> throw new IllegalArgumentException("Unsupported state: " + state);
|
default -> throw new IllegalArgumentException("Unsupported state: " + state);
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
|
||||||
|
class SourcePresentState extends AudioStreamStateHandler {
|
||||||
|
@VisibleForTesting
|
||||||
|
static final int AUDIO_STREAM_SOURCE_PRESENT_STATE_SUMMARY = R.string.audio_streams_present_now;
|
||||||
|
|
||||||
|
@Nullable private static SourcePresentState sInstance = null;
|
||||||
|
|
||||||
|
SourcePresentState() {}
|
||||||
|
|
||||||
|
static SourcePresentState getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new SourcePresentState();
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void performAction(
|
||||||
|
AudioStreamPreference preference,
|
||||||
|
AudioStreamsProgressCategoryController controller,
|
||||||
|
AudioStreamsHelper helper) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getSummary() {
|
||||||
|
return AUDIO_STREAM_SOURCE_PRESENT_STATE_SUMMARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Preference.OnPreferenceClickListener getOnClickListener(
|
||||||
|
AudioStreamsProgressCategoryController controller) {
|
||||||
|
return preference -> {
|
||||||
|
var p = (AudioStreamPreference) preference;
|
||||||
|
Bundle broadcast = new Bundle();
|
||||||
|
broadcast.putString(
|
||||||
|
AudioStreamDetailsFragment.BROADCAST_NAME_ARG, (String) p.getTitle());
|
||||||
|
broadcast.putInt(
|
||||||
|
AudioStreamDetailsFragment.BROADCAST_ID_ARG, p.getAudioStreamBroadcastId());
|
||||||
|
|
||||||
|
new SubSettingLauncher(p.getContext())
|
||||||
|
.setTitleRes(R.string.audio_streams_detail_page_title)
|
||||||
|
.setDestination(AudioStreamDetailsFragment.class.getName())
|
||||||
|
.setSourceMetricsCategory(
|
||||||
|
!(controller.getFragment() instanceof DashboardFragment)
|
||||||
|
? SettingsEnums.PAGE_UNKNOWN
|
||||||
|
: ((DashboardFragment) controller.getFragment())
|
||||||
|
.getMetricsCategory())
|
||||||
|
.setArguments(broadcast)
|
||||||
|
.launch();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
AudioStreamsProgressCategoryController.AudioStreamState getStateEnum() {
|
||||||
|
return AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
@@ -37,7 +37,6 @@ import com.android.settingslib.core.instrumentation.Instrumentable;
|
|||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
|
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
|
||||||
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
|
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
|
||||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instrumented fragment that logs visibility state.
|
* Instrumented fragment that logs visibility state.
|
||||||
|
137
src/com/android/settings/core/ObservablePreferenceFragment.java
Normal file
137
src/com/android/settings/core/ObservablePreferenceFragment.java
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.settings.core;
|
||||||
|
|
||||||
|
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_START;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
|
||||||
|
|
||||||
|
import android.annotation.CallSuper;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.preference.PreferenceFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference fragment that has hooks to observe fragment lifecycle events.
|
||||||
|
*/
|
||||||
|
public abstract class ObservablePreferenceFragment extends PreferenceFragment
|
||||||
|
implements LifecycleOwner {
|
||||||
|
|
||||||
|
private final Lifecycle mLifecycle = new Lifecycle(this);
|
||||||
|
|
||||||
|
public Lifecycle getSettingsLifecycle() {
|
||||||
|
return mLifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
mLifecycle.onAttach(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
mLifecycle.onCreate(savedInstanceState);
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_CREATE);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
|
||||||
|
mLifecycle.setPreferenceScreen(preferenceScreen);
|
||||||
|
super.setPreferenceScreen(preferenceScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
mLifecycle.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_START);
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_RESUME);
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_PAUSE);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_STOP);
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
mLifecycle.handleLifecycleEvent(ON_DESTROY);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||||
|
mLifecycle.onCreateOptionsMenu(menu, inflater);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onPrepareOptionsMenu(final Menu menu) {
|
||||||
|
mLifecycle.onPrepareOptionsMenu(menu);
|
||||||
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(final MenuItem menuItem) {
|
||||||
|
boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem);
|
||||||
|
if (!lifecycleHandled) {
|
||||||
|
return super.onOptionsItemSelected(menuItem);
|
||||||
|
}
|
||||||
|
return lifecycleHandled;
|
||||||
|
}
|
||||||
|
}
|
@@ -27,13 +27,13 @@ import com.android.settings.R
|
|||||||
import com.android.settings.SettingsPreferenceFragment
|
import com.android.settings.SettingsPreferenceFragment
|
||||||
import com.android.settings.dashboard.DashboardFragment
|
import com.android.settings.dashboard.DashboardFragment
|
||||||
import com.android.settings.flags.Flags
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.telephony.SimRepository
|
||||||
import com.android.settings.network.telephony.euicc.EuiccRepository
|
import com.android.settings.network.telephony.euicc.EuiccRepository
|
||||||
import com.android.settings.search.BaseSearchIndexProvider
|
import com.android.settings.search.BaseSearchIndexProvider
|
||||||
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
|
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
|
||||||
import com.android.settings.spa.network.NetworkCellularGroupProvider
|
import com.android.settings.spa.network.NetworkCellularGroupProvider
|
||||||
import com.android.settingslib.search.SearchIndexable
|
import com.android.settingslib.search.SearchIndexable
|
||||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
|
||||||
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
|
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
|
||||||
|
|
||||||
@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
|
@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
|
||||||
@@ -85,10 +85,11 @@ class MobileNetworkListFragment : DashboardFragment() {
|
|||||||
val SEARCH_INDEX_DATA_PROVIDER = SearchIndexProvider()
|
val SEARCH_INDEX_DATA_PROVIDER = SearchIndexProvider()
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
class SearchIndexProvider : BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
|
class SearchIndexProvider(
|
||||||
|
private val simRepositoryFactory: (Context) -> SimRepository = ::SimRepository
|
||||||
|
) : BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
|
||||||
public override fun isPageSearchEnabled(context: Context): Boolean =
|
public override fun isPageSearchEnabled(context: Context): Boolean =
|
||||||
SubscriptionUtil.isSimHardwareVisible(context) &&
|
simRepositoryFactory(context).showMobileNetworkPage()
|
||||||
context.userManager.isAdminUser
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,162 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.network;
|
|
||||||
|
|
||||||
import static android.os.UserHandle.myUserId;
|
|
||||||
import static android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
|
|
||||||
|
|
||||||
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
|
||||||
|
|
||||||
import static androidx.lifecycle.Lifecycle.Event;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.telephony.PhoneStateListener;
|
|
||||||
import android.telephony.ServiceState;
|
|
||||||
import android.telephony.TelephonyCallback;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.lifecycle.LifecycleObserver;
|
|
||||||
import androidx.lifecycle.OnLifecycleEvent;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
|
||||||
|
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
|
||||||
import com.android.settings.network.telephony.MobileNetworkUtils;
|
|
||||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
|
||||||
import com.android.settingslib.RestrictedPreference;
|
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
|
||||||
|
|
||||||
public class MobileNetworkPreferenceController extends AbstractPreferenceController
|
|
||||||
implements PreferenceControllerMixin, LifecycleObserver {
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings";
|
|
||||||
|
|
||||||
private final boolean mIsSecondaryUser;
|
|
||||||
private final TelephonyManager mTelephonyManager;
|
|
||||||
private final UserManager mUserManager;
|
|
||||||
private Preference mPreference;
|
|
||||||
@VisibleForTesting
|
|
||||||
MobileNetworkTelephonyCallback mTelephonyCallback;
|
|
||||||
|
|
||||||
private BroadcastReceiver mAirplanModeChangedReceiver;
|
|
||||||
|
|
||||||
public MobileNetworkPreferenceController(Context context) {
|
|
||||||
super(context);
|
|
||||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
|
||||||
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
|
||||||
mIsSecondaryUser = !mUserManager.isAdminUser();
|
|
||||||
|
|
||||||
mAirplanModeChangedReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
updateState(mPreference);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return !isUserRestricted() && !Utils.isWifiOnly(mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUserRestricted() {
|
|
||||||
return mIsSecondaryUser ||
|
|
||||||
RestrictedLockUtilsInternal.hasBaseUserRestriction(
|
|
||||||
mContext,
|
|
||||||
DISALLOW_CONFIG_MOBILE_NETWORKS,
|
|
||||||
myUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayPreference(PreferenceScreen screen) {
|
|
||||||
super.displayPreference(screen);
|
|
||||||
mPreference = screen.findPreference(getPreferenceKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPreferenceKey() {
|
|
||||||
return KEY_MOBILE_NETWORK_SETTINGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MobileNetworkTelephonyCallback extends TelephonyCallback implements
|
|
||||||
TelephonyCallback.ServiceStateListener {
|
|
||||||
@Override
|
|
||||||
public void onServiceStateChanged(ServiceState serviceState) {
|
|
||||||
updateState(mPreference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnLifecycleEvent(Event.ON_START)
|
|
||||||
public void onStart() {
|
|
||||||
if (isAvailable()) {
|
|
||||||
if (mTelephonyCallback == null) {
|
|
||||||
mTelephonyCallback = new MobileNetworkTelephonyCallback();
|
|
||||||
}
|
|
||||||
mTelephonyManager.registerTelephonyCallback(
|
|
||||||
mContext.getMainExecutor(), mTelephonyCallback);
|
|
||||||
}
|
|
||||||
if (mAirplanModeChangedReceiver != null) {
|
|
||||||
mContext.registerReceiver(mAirplanModeChangedReceiver,
|
|
||||||
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnLifecycleEvent(Event.ON_STOP)
|
|
||||||
public void onStop() {
|
|
||||||
if (mTelephonyCallback != null) {
|
|
||||||
mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
|
|
||||||
}
|
|
||||||
if (mAirplanModeChangedReceiver != null) {
|
|
||||||
mContext.unregisterReceiver(mAirplanModeChangedReceiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateState(Preference preference) {
|
|
||||||
super.updateState(preference);
|
|
||||||
|
|
||||||
if (preference instanceof RestrictedPreference &&
|
|
||||||
((RestrictedPreference) preference).isDisabledByAdmin()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
preference.setEnabled(Settings.Global.getInt(
|
|
||||||
mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
||||||
if (KEY_MOBILE_NETWORK_SETTINGS.equals(preference.getKey())) {
|
|
||||||
final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
|
|
||||||
intent.setPackage(SETTINGS_PACKAGE_NAME);
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getSummary() {
|
|
||||||
return MobileNetworkUtils.getCurrentCarrierNameForDisplay(mContext);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -21,7 +21,6 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.UserManager;
|
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
import android.telephony.euicc.EuiccManager;
|
import android.telephony.euicc.EuiccManager;
|
||||||
|
|
||||||
@@ -35,10 +34,10 @@ import androidx.preference.PreferenceScreen;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
import com.android.settings.core.PreferenceControllerMixin;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
import com.android.settings.network.telephony.SimRepository;
|
||||||
import com.android.settings.network.telephony.euicc.EuiccRepository;
|
import com.android.settings.network.telephony.euicc.EuiccRepository;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.RestrictedPreference;
|
import com.android.settingslib.RestrictedPreference;
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
|
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
|
||||||
@@ -56,7 +55,6 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
|
|||||||
private static final String KEY = "mobile_network_list";
|
private static final String KEY = "mobile_network_list";
|
||||||
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private UserManager mUserManager;
|
|
||||||
private RestrictedPreference mPreference;
|
private RestrictedPreference mPreference;
|
||||||
|
|
||||||
private MobileNetworkRepository mMobileNetworkRepository;
|
private MobileNetworkRepository mMobileNetworkRepository;
|
||||||
@@ -85,7 +83,6 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
|
|||||||
LifecycleOwner lifecycleOwner) {
|
LifecycleOwner lifecycleOwner) {
|
||||||
super(context);
|
super(context);
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
||||||
mUserManager = context.getSystemService(UserManager.class);
|
|
||||||
mLifecycleOwner = lifecycleOwner;
|
mLifecycleOwner = lifecycleOwner;
|
||||||
mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
|
mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
|
||||||
mIsAirplaneModeOn = mMobileNetworkRepository.isAirplaneModeOn();
|
mIsAirplaneModeOn = mMobileNetworkRepository.isAirplaneModeOn();
|
||||||
@@ -185,8 +182,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
return SubscriptionUtil.isSimHardwareVisible(mContext) &&
|
return new SimRepository(mContext).showMobileNetworkPage();
|
||||||
!Utils.isWifiOnly(mContext) && mUserManager.isAdminUser();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.network;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.BidiFormatter;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.Utils;
|
|
||||||
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
|
|
||||||
import com.android.settings.core.BasePreferenceController;
|
|
||||||
|
|
||||||
public class TopLevelNetworkEntryPreferenceController extends BasePreferenceController {
|
|
||||||
|
|
||||||
private final MobileNetworkPreferenceController mMobileNetworkPreferenceController;
|
|
||||||
|
|
||||||
public TopLevelNetworkEntryPreferenceController(Context context, String preferenceKey) {
|
|
||||||
super(context, preferenceKey);
|
|
||||||
mMobileNetworkPreferenceController = new MobileNetworkPreferenceController(mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAvailabilityStatus() {
|
|
||||||
// TODO(b/281597506): Update the ActivityEmbeddingUtils.isEmbeddingActivityEnabled
|
|
||||||
// while getting the new API.
|
|
||||||
return (Utils.isDemoUser(mContext)
|
|
||||||
&& !ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext))
|
|
||||||
? UNSUPPORTED_ON_DEVICE : AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getSummary() {
|
|
||||||
if (mMobileNetworkPreferenceController.isAvailable()) {
|
|
||||||
return BidiFormatter.getInstance()
|
|
||||||
.unicodeWrap(mContext.getString(R.string.network_dashboard_summary_mobile));
|
|
||||||
} else {
|
|
||||||
return BidiFormatter.getInstance()
|
|
||||||
.unicodeWrap(mContext.getString(R.string.network_dashboard_summary_no_mobile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.network
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.BidiFormatter
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.Utils
|
||||||
|
import com.android.settings.activityembedding.ActivityEmbeddingUtils
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.network.telephony.SimRepository
|
||||||
|
|
||||||
|
class TopLevelNetworkEntryPreferenceController
|
||||||
|
@JvmOverloads
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
preferenceKey: String,
|
||||||
|
private val simRepository: SimRepository = SimRepository(context),
|
||||||
|
private val isDemoUser: () -> Boolean = { Utils.isDemoUser(context) },
|
||||||
|
private val isEmbeddingActivityEnabled: () -> Boolean = {
|
||||||
|
ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)
|
||||||
|
},
|
||||||
|
) : BasePreferenceController(context, preferenceKey) {
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(): Int {
|
||||||
|
// TODO(b/281597506): Update the ActivityEmbeddingUtils.isEmbeddingActivityEnabled
|
||||||
|
// while getting the new API.
|
||||||
|
return if (isDemoUser() && !isEmbeddingActivityEnabled()) {
|
||||||
|
UNSUPPORTED_ON_DEVICE
|
||||||
|
} else {
|
||||||
|
AVAILABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSummary(): CharSequence {
|
||||||
|
val summaryResId =
|
||||||
|
if (simRepository.showMobileNetworkPage()) {
|
||||||
|
R.string.network_dashboard_summary_mobile
|
||||||
|
} else {
|
||||||
|
R.string.network_dashboard_summary_no_mobile
|
||||||
|
}
|
||||||
|
return BidiFormatter.getInstance().unicodeWrap(mContext.getString(summaryResId))
|
||||||
|
}
|
||||||
|
}
|
30
src/com/android/settings/network/telephony/SimRepository.kt
Normal file
30
src/com/android/settings/network/telephony/SimRepository.kt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
|
||||||
|
class SimRepository(context: Context) {
|
||||||
|
private val packageManager = context.packageManager
|
||||||
|
private val userManager = context.userManager
|
||||||
|
|
||||||
|
/** Gets whether we show mobile network settings page to the current user. */
|
||||||
|
fun showMobileNetworkPage(): Boolean =
|
||||||
|
packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && userManager.isAdminUser
|
||||||
|
}
|
@@ -76,7 +76,7 @@ public class ZenModeBehaviorFooterPreferenceController extends AbstractZenModePr
|
|||||||
|
|
||||||
// DND turned on by an automatic rule with deprecated zen mode
|
// DND turned on by an automatic rule with deprecated zen mode
|
||||||
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
||||||
if (automaticRule.isAutomaticActive() && isDeprecatedZenMode(
|
if (automaticRule.isActive() && isDeprecatedZenMode(
|
||||||
automaticRule.zenMode)) {
|
automaticRule.zenMode)) {
|
||||||
ComponentName component = automaticRule.component;
|
ComponentName component = automaticRule.component;
|
||||||
if (component != null) {
|
if (component != null) {
|
||||||
|
@@ -153,7 +153,7 @@ public class ZenModeSettingsFooterPreferenceController extends AbstractZenModePr
|
|||||||
|
|
||||||
// DND turned on by an automatic rule
|
// DND turned on by an automatic rule
|
||||||
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
||||||
if (automaticRule.isAutomaticActive()) {
|
if (automaticRule.isActive()) {
|
||||||
// set footer if 3rd party rule
|
// set footer if 3rd party rule
|
||||||
if (!mZenModeConfigWrapper.isTimeRule(automaticRule.conditionId)) {
|
if (!mZenModeConfigWrapper.isTimeRule(automaticRule.conditionId)) {
|
||||||
return mContext.getString(R.string.zen_mode_settings_dnd_automatic_rule,
|
return mContext.getString(R.string.zen_mode_settings_dnd_automatic_rule,
|
||||||
@@ -180,7 +180,7 @@ public class ZenModeSettingsFooterPreferenceController extends AbstractZenModePr
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
|
||||||
if (automaticRule.isAutomaticActive()) {
|
if (automaticRule.isActive()) {
|
||||||
zenRules.add(automaticRule);
|
zenRules.add(automaticRule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ import androidx.preference.Preference;
|
|||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.settings.core.ObservablePreferenceFragment;
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
import com.android.settings.core.PreferenceControllerMixin;
|
||||||
import com.android.settings.users.OwnerInfoSettings;
|
import com.android.settings.users.OwnerInfoSettings;
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
@@ -31,7 +32,6 @@ import com.android.settingslib.RestrictedLockUtilsInternal;
|
|||||||
import com.android.settingslib.RestrictedPreference;
|
import com.android.settingslib.RestrictedPreference;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||||
|
|
||||||
public class OwnerInfoPreferenceController extends AbstractPreferenceController
|
public class OwnerInfoPreferenceController extends AbstractPreferenceController
|
||||||
|
@@ -24,6 +24,7 @@ import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
|
|||||||
|
|
||||||
import android.annotation.ColorInt;
|
import android.annotation.ColorInt;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -47,6 +48,7 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.SubSettings;
|
import com.android.settings.SubSettings;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
|
import com.android.settings.accessibility.AccessibilitySlicePreferenceController;
|
||||||
import com.android.settings.core.BasePreferenceController;
|
import com.android.settings.core.BasePreferenceController;
|
||||||
import com.android.settings.core.SliderPreferenceController;
|
import com.android.settings.core.SliderPreferenceController;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
@@ -448,7 +450,17 @@ public class SliceBuilderUtils {
|
|||||||
iconResource = R.drawable.ic_settings_accent;
|
iconResource = R.drawable.ic_settings_accent;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return IconCompat.createWithResource(context, iconResource);
|
// LINT.IfChange(createA11yIcon)
|
||||||
|
if (AccessibilitySlicePreferenceController.class.getName().equals(
|
||||||
|
data.getPreferenceController())) {
|
||||||
|
ComponentName serviceComponent = ComponentName.unflattenFromString(data.getKey());
|
||||||
|
return IconCompat.createWithResource(
|
||||||
|
context.createPackageContext(serviceComponent.getPackageName(), 0),
|
||||||
|
iconResource);
|
||||||
|
// LINT.ThenChange()
|
||||||
|
} else {
|
||||||
|
return IconCompat.createWithResource(context, iconResource);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Falling back to settings icon because there is an error getting slice icon "
|
Log.w(TAG, "Falling back to settings icon because there is an error getting slice icon "
|
||||||
+ data.getUri(), e);
|
+ data.getUri(), e);
|
||||||
|
@@ -274,6 +274,12 @@ class SliceDataConverter {
|
|||||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||||
final String packageName = serviceInfo.packageName;
|
final String packageName = serviceInfo.packageName;
|
||||||
final ComponentName componentName = new ComponentName(packageName, serviceInfo.name);
|
final ComponentName componentName = new ComponentName(packageName, serviceInfo.name);
|
||||||
|
|
||||||
|
// If we change the flattenedName that is used to be set as a key of the Slice, we
|
||||||
|
// need to make corresponding change in SliceBuilderUtils, since we rely on the
|
||||||
|
// the A11y Service Slice's key to be a ComponentName to get the correct package name
|
||||||
|
// to grab the icon belongs to that package.
|
||||||
|
// LINT.IfChange
|
||||||
final String flattenedName = componentName.flattenToString();
|
final String flattenedName = componentName.flattenToString();
|
||||||
|
|
||||||
if (!a11yServiceNames.contains(flattenedName)) {
|
if (!a11yServiceNames.contains(flattenedName)) {
|
||||||
@@ -287,6 +293,7 @@ class SliceDataConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sliceDataBuilder.setKey(flattenedName)
|
sliceDataBuilder.setKey(flattenedName)
|
||||||
|
// LINT.ThenChange(SliceBuilderUtils.java:createA11yIcon)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setUri(new Uri.Builder()
|
.setUri(new Uri.Builder()
|
||||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.sound;
|
||||||
|
|
||||||
|
import android.app.Flags;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
|
||||||
|
public class TopLevelSoundPreferenceController extends BasePreferenceController {
|
||||||
|
|
||||||
|
public TopLevelSoundPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(Preference preference) {
|
||||||
|
super.updateState(preference);
|
||||||
|
preference.setSummary(Flags.modesApi() && Flags.modesUi()
|
||||||
|
? R.string.sound_dashboard_summary
|
||||||
|
: R.string.sound_dashboard_summary_with_dnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return AVAILABLE;
|
||||||
|
}
|
||||||
|
}
|
@@ -48,9 +48,9 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.flags.Flags
|
import com.android.settings.flags.Flags
|
||||||
import com.android.settings.network.SubscriptionInfoListViewModel
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
import com.android.settings.network.SubscriptionUtil
|
|
||||||
import com.android.settings.network.telephony.DataSubscriptionRepository
|
import com.android.settings.network.telephony.DataSubscriptionRepository
|
||||||
import com.android.settings.network.telephony.MobileDataRepository
|
import com.android.settings.network.telephony.MobileDataRepository
|
||||||
|
import com.android.settings.network.telephony.SimRepository
|
||||||
import com.android.settings.network.telephony.requireSubscriptionManager
|
import com.android.settings.network.telephony.requireSubscriptionManager
|
||||||
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
|
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
|
||||||
import com.android.settings.spa.search.SearchablePage
|
import com.android.settings.spa.search.SearchablePage
|
||||||
@@ -66,7 +66,6 @@ import com.android.settingslib.spa.widget.preference.PreferenceModel
|
|||||||
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
||||||
import com.android.settingslib.spa.widget.ui.Category
|
import com.android.settingslib.spa.widget.ui.Category
|
||||||
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
|
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
|
||||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
|
||||||
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
|
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -213,10 +212,7 @@ open class NetworkCellularGroupProvider : SettingsPageProvider, SearchablePage {
|
|||||||
const val fileName = "NetworkCellularGroupProvider"
|
const val fileName = "NetworkCellularGroupProvider"
|
||||||
|
|
||||||
private fun isPageSearchable(context: Context) =
|
private fun isPageSearchable(context: Context) =
|
||||||
Flags.isDualSimOnboardingEnabled() &&
|
Flags.isDualSimOnboardingEnabled() && SimRepository(context).showMobileNetworkPage()
|
||||||
SubscriptionUtil.isSimHardwareVisible(context) &&
|
|
||||||
!com.android.settingslib.Utils.isWifiOnly(context) &&
|
|
||||||
context.userManager.isAdminUser
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,7 +42,6 @@ import android.platform.test.annotations.EnableFlags;
|
|||||||
import android.platform.test.flag.junit.SetFlagsRule;
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.view.accessibility.Flags;
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@@ -50,6 +49,7 @@ import androidx.test.core.app.ApplicationProvider;
|
|||||||
import com.android.internal.accessibility.util.AccessibilityUtils;
|
import com.android.internal.accessibility.util.AccessibilityUtils;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
import com.android.settings.testutils.XmlTestUtils;
|
import com.android.settings.testutils.XmlTestUtils;
|
||||||
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
|
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
|
||||||
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
|
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
|
||||||
@@ -78,6 +78,7 @@ import org.robolectric.android.controller.ActivityController;
|
|||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadow.api.Shadow;
|
import org.robolectric.shadow.api.Shadow;
|
||||||
import org.robolectric.shadows.ShadowContentResolver;
|
import org.robolectric.shadows.ShadowContentResolver;
|
||||||
|
import org.robolectric.shadows.ShadowLooper;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -155,6 +156,53 @@ public class AccessibilitySettingsTest {
|
|||||||
assertThat(indexableRawList).isNull();
|
assertThat(indexableRawList).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DisableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
|
||||||
|
@Test
|
||||||
|
public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOff_returnEmpty() {
|
||||||
|
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||||
|
List.of(mServiceInfo));
|
||||||
|
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
|
||||||
|
List.of(getMockAccessibilityShortcutInfo()));
|
||||||
|
|
||||||
|
assertThat(AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
|
||||||
|
mContext, /* enabled= */ true))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
|
||||||
|
@Test
|
||||||
|
public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOn_returnRawDataForInstalledA11yFeatures() {
|
||||||
|
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||||
|
List.of(mServiceInfo));
|
||||||
|
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
|
||||||
|
List.of(getMockAccessibilityShortcutInfo()));
|
||||||
|
final AccessibilitySearchFeatureProvider featureProvider =
|
||||||
|
FakeFeatureFactory.setupForTest().getAccessibilitySearchFeatureProvider();
|
||||||
|
final String synonyms = "fake keyword1, fake keyword2";
|
||||||
|
when(featureProvider.getSynonymsForComponent(mContext, ACTIVITY_COMPONENT_NAME))
|
||||||
|
.thenReturn("");
|
||||||
|
when(featureProvider.getSynonymsForComponent(mContext, SERVICE_COMPONENT_NAME))
|
||||||
|
.thenReturn(synonyms);
|
||||||
|
|
||||||
|
final List<SearchIndexableRaw> indexableRawDataList =
|
||||||
|
AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
|
||||||
|
mContext, /* enabled= */ true);
|
||||||
|
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||||
|
|
||||||
|
assertThat(indexableRawDataList).hasSize(2);
|
||||||
|
SearchIndexableRaw a11yActivityIndexableData = indexableRawDataList.get(0);
|
||||||
|
assertThat(a11yActivityIndexableData.key).isEqualTo(
|
||||||
|
ACTIVITY_COMPONENT_NAME.flattenToString());
|
||||||
|
assertThat(a11yActivityIndexableData.title).isEqualTo(DEFAULT_LABEL);
|
||||||
|
assertThat(a11yActivityIndexableData.keywords).isEmpty();
|
||||||
|
|
||||||
|
SearchIndexableRaw a11yServiceIndexableData = indexableRawDataList.get(1);
|
||||||
|
assertThat(a11yServiceIndexableData.key).isEqualTo(
|
||||||
|
SERVICE_COMPONENT_NAME.flattenToString());
|
||||||
|
assertThat(a11yServiceIndexableData.title).isEqualTo(DEFAULT_LABEL);
|
||||||
|
assertThat(a11yServiceIndexableData.keywords).isEqualTo(synonyms);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getServiceSummary_serviceCrash_showsStopped() {
|
public void getServiceSummary_serviceCrash_showsStopped() {
|
||||||
mServiceInfo.crashed = true;
|
mServiceInfo.crashed = true;
|
||||||
@@ -328,7 +376,7 @@ public class AccessibilitySettingsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
|
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
|
||||||
public void onCreate_flagDisabled_haveRegisterToSpecificUrisAndActions() {
|
public void onCreate_flagDisabled_haveRegisterToSpecificUrisAndActions() {
|
||||||
setupFragment();
|
setupFragment();
|
||||||
|
|
||||||
@@ -341,7 +389,7 @@ public class AccessibilitySettingsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
|
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
|
||||||
public void onCreate_flagEnabled_haveRegisterToSpecificUrisAndActions() {
|
public void onCreate_flagEnabled_haveRegisterToSpecificUrisAndActions() {
|
||||||
setupFragment();
|
setupFragment();
|
||||||
|
|
||||||
@@ -415,7 +463,7 @@ public class AccessibilitySettingsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@EnableFlags(com.android.settings.accessibility.Flags.FLAG_CHECK_PREBUNDLED_IS_PREINSTALLED)
|
@EnableFlags(Flags.FLAG_CHECK_PREBUNDLED_IS_PREINSTALLED)
|
||||||
public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
|
public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
|
||||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||||
List.of(getMockAccessibilityServiceInfo(
|
List.of(getMockAccessibilityServiceInfo(
|
||||||
|
@@ -34,6 +34,7 @@ import android.util.AttributeSet;
|
|||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.testutils.shadow.ShadowSystemSettings;
|
import com.android.settings.testutils.shadow.ShadowSystemSettings;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -162,7 +163,8 @@ public class BalanceSeekBarTest {
|
|||||||
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
||||||
|
|
||||||
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
||||||
mContext.getString(R.string.audio_seek_bar_state_left_first, 50, 50));
|
mContext.getString(R.string.audio_seek_bar_state_left_first,
|
||||||
|
Utils.formatPercentage(50), Utils.formatPercentage(50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -177,7 +179,8 @@ public class BalanceSeekBarTest {
|
|||||||
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
||||||
|
|
||||||
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
||||||
mContext.getString(R.string.audio_seek_bar_state_right_first, 50, 50));
|
mContext.getString(R.string.audio_seek_bar_state_right_first,
|
||||||
|
Utils.formatPercentage(50), Utils.formatPercentage(50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -189,7 +192,8 @@ public class BalanceSeekBarTest {
|
|||||||
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
||||||
|
|
||||||
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
||||||
mContext.getString(R.string.audio_seek_bar_state_left_first, 75, 25));
|
mContext.getString(R.string.audio_seek_bar_state_left_first,
|
||||||
|
Utils.formatPercentage(75), Utils.formatPercentage(25)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -201,7 +205,8 @@ public class BalanceSeekBarTest {
|
|||||||
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
|
||||||
|
|
||||||
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
assertThat(mSeekBar.getStateDescription()).isEqualTo(
|
||||||
mContext.getString(R.string.audio_seek_bar_state_right_first, 75, 25));
|
mContext.getString(R.string.audio_seek_bar_state_right_first,
|
||||||
|
Utils.formatPercentage(75), Utils.formatPercentage(25)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// method to get the center from BalanceSeekBar for testing setMax().
|
// method to get the center from BalanceSeekBar for testing setMax().
|
||||||
|
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.settings.biometrics.fingerprint
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import com.android.settings.overlay.FeatureFactory
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.robolectric.Robolectric
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.Shadows
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class FingerprintEnrollTest {
|
||||||
|
|
||||||
|
private lateinit var featureFactory: FeatureFactory
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val INTENT_KEY = "testKey"
|
||||||
|
const val INTENT_VALUE = "testValue"
|
||||||
|
val INTENT = Intent().apply {
|
||||||
|
putExtra(INTENT_KEY, INTENT_VALUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val activityProvider = FingerprintEnrollActivityClassProvider()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
featureFactory = FakeFeatureFactory.setupForTest()
|
||||||
|
`when`(featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider)
|
||||||
|
.thenReturn(activityProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupActivity(activityClass: Class<out FingerprintEnroll>): FingerprintEnroll {
|
||||||
|
return Robolectric.buildActivity(activityClass, INTENT).create().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFinishAndLaunchDefaultActivity() {
|
||||||
|
// Run
|
||||||
|
val activity = setupActivity(FingerprintEnroll::class.java)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verifyLaunchNextActivity(activity, activityProvider.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFinishAndLaunchSetupActivity() {
|
||||||
|
// Run
|
||||||
|
val activity = setupActivity(FingerprintEnroll.SetupActivity::class.java)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verifyLaunchNextActivity(activity, activityProvider.setup)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFinishAndLaunchInternalActivity() {
|
||||||
|
// Run
|
||||||
|
val activity = setupActivity(FingerprintEnroll.InternalActivity::class.java)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verifyLaunchNextActivity(activity, activityProvider.internal)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyLaunchNextActivity(
|
||||||
|
currentActivityInstance : FingerprintEnroll,
|
||||||
|
nextActivityClass: Class<out Activity>
|
||||||
|
) {
|
||||||
|
assertThat(currentActivityInstance.isFinishing).isTrue()
|
||||||
|
val nextActivityIntent = Shadows.shadowOf(currentActivityInstance).nextStartedActivity
|
||||||
|
assertThat(nextActivityIntent.component!!.className).isEqualTo(nextActivityClass.name)
|
||||||
|
assertThat(nextActivityIntent.extras!!.size()).isEqualTo(1)
|
||||||
|
assertThat(nextActivityIntent.getStringExtra(INTENT_KEY)).isEqualTo(INTENT_VALUE)
|
||||||
|
}
|
||||||
|
}
|
@@ -124,10 +124,11 @@ class DeviceDetailsFragmentFormatterTest {
|
|||||||
listOf(
|
listOf(
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
||||||
"bluetooth_device_header"
|
highlighted = false,
|
||||||
|
preferenceKey = "bluetooth_device_header"
|
||||||
),
|
),
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"),
|
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, highlighted = false, preferenceKey = "action_buttons"),
|
||||||
),
|
),
|
||||||
listOf(),
|
listOf(),
|
||||||
null))
|
null))
|
||||||
@@ -157,7 +158,7 @@ class DeviceDetailsFragmentFormatterTest {
|
|||||||
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
`when`(repository.getDeviceSettingsConfig(cachedDevice))
|
||||||
.thenReturn(
|
.thenReturn(
|
||||||
DeviceSettingConfigModel(
|
DeviceSettingConfigModel(
|
||||||
listOf(), listOf(), DeviceSettingConfigItemModel.AppProvidedItem(12345)))
|
listOf(), listOf(), DeviceSettingConfigItemModel.AppProvidedItem(12345, false)))
|
||||||
val intent = Intent().apply {
|
val intent = Intent().apply {
|
||||||
setAction(Intent.ACTION_VIEW)
|
setAction(Intent.ACTION_VIEW)
|
||||||
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
@@ -206,10 +207,10 @@ class DeviceDetailsFragmentFormatterTest {
|
|||||||
listOf(
|
listOf(
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
||||||
"bluetooth_device_header"),
|
highlighted = false, preferenceKey = "bluetooth_device_header"),
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
|
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
|
||||||
"keyboard_settings"),
|
highlighted = false, preferenceKey = "keyboard_settings"),
|
||||||
),
|
),
|
||||||
listOf(),
|
listOf(),
|
||||||
null))
|
null))
|
||||||
@@ -230,12 +231,14 @@ class DeviceDetailsFragmentFormatterTest {
|
|||||||
listOf(
|
listOf(
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
|
||||||
"bluetooth_device_header"),
|
highlighted = false,
|
||||||
|
preferenceKey = "bluetooth_device_header"),
|
||||||
DeviceSettingConfigItemModel.AppProvidedItem(
|
DeviceSettingConfigItemModel.AppProvidedItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_ANC),
|
DeviceSettingId.DEVICE_SETTING_ID_ANC, highlighted = false),
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
|
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
|
||||||
"keyboard_settings"),
|
highlighted = false,
|
||||||
|
preferenceKey = "keyboard_settings"),
|
||||||
),
|
),
|
||||||
listOf(),
|
listOf(),
|
||||||
null))
|
null))
|
||||||
|
@@ -246,11 +246,11 @@ class BluetoothDeviceDetailsViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getLatestLayout(layout: DeviceSettingLayout): List<List<Int>> {
|
private fun getLatestLayout(layout: DeviceSettingLayout): List<List<Int>> {
|
||||||
var latestLayout = MutableList(layout.rows.size) { emptyList<Int>() }
|
val latestLayout = MutableList(layout.rows.size) { emptyList<Int>() }
|
||||||
for (i in layout.rows.indices) {
|
for (i in layout.rows.indices) {
|
||||||
layout.rows[i]
|
layout.rows[i]
|
||||||
.settingIds
|
.columns
|
||||||
.onEach { latestLayout[i] = it }
|
.onEach { latestLayout[i] = it.map { c -> c.settingId } }
|
||||||
.launchIn(testScope.backgroundScope)
|
.launchIn(testScope.backgroundScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,15 +278,15 @@ class BluetoothDeviceDetailsViewModelTest {
|
|||||||
DeviceSettingModel.ActionSwitchPreference(cachedDevice, settingId, "title")
|
DeviceSettingModel.ActionSwitchPreference(cachedDevice, settingId, "title")
|
||||||
|
|
||||||
private fun buildRemoteSettingItem(settingId: Int) =
|
private fun buildRemoteSettingItem(settingId: Int) =
|
||||||
DeviceSettingConfigItemModel.AppProvidedItem(settingId)
|
DeviceSettingConfigItemModel.AppProvidedItem(settingId, false)
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val BUILTIN_SETTING_ITEM_1 =
|
val BUILTIN_SETTING_ITEM_1 =
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_HEADER, "bluetooth_device_header")
|
DeviceSettingId.DEVICE_SETTING_ID_HEADER, false, "bluetooth_device_header")
|
||||||
val BUILDIN_SETTING_ITEM_2 =
|
val BUILDIN_SETTING_ITEM_2 =
|
||||||
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
|
||||||
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons")
|
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, false, "action_buttons")
|
||||||
val SETTING_ITEM_HELP = DeviceSettingConfigItemModel.AppProvidedItem(12345)
|
val SETTING_ITEM_HELP = DeviceSettingConfigItemModel.AppProvidedItem(12345, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -34,6 +36,7 @@ import android.bluetooth.BluetoothLeBroadcastAssistant;
|
|||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
@@ -72,8 +75,8 @@ import java.util.concurrent.Executor;
|
|||||||
ShadowAudioStreamsHelper.class,
|
ShadowAudioStreamsHelper.class,
|
||||||
})
|
})
|
||||||
public class AudioStreamButtonControllerTest {
|
public class AudioStreamButtonControllerTest {
|
||||||
|
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
private static final String KEY = "audio_stream_button";
|
private static final String KEY = "audio_stream_button";
|
||||||
private static final int BROADCAST_ID = 1;
|
private static final int BROADCAST_ID = 1;
|
||||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
@@ -83,6 +86,7 @@ public class AudioStreamButtonControllerTest {
|
|||||||
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
@Mock private AudioStreamsRepository mRepository;
|
@Mock private AudioStreamsRepository mRepository;
|
||||||
@Mock private ActionButtonsPreference mPreference;
|
@Mock private ActionButtonsPreference mPreference;
|
||||||
|
@Mock private BluetoothDevice mSourceDevice;
|
||||||
private Lifecycle mLifecycle;
|
private Lifecycle mLifecycle;
|
||||||
private LifecycleOwner mLifecycleOwner;
|
private LifecycleOwner mLifecycleOwner;
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
@@ -90,6 +94,7 @@ public class AudioStreamButtonControllerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
|
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
@@ -254,6 +259,33 @@ public class AudioStreamButtonControllerTest {
|
|||||||
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
|
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
||||||
|
when(state.getBroadcastId()).thenReturn(BROADCAST_ID);
|
||||||
|
when(state.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
when(state.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
when(mAudioStreamsHelper.getAllPresentSources()).thenReturn(List.of(state));
|
||||||
|
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
|
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
|
||||||
|
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider, never())
|
||||||
|
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED), anyInt());
|
||||||
|
|
||||||
|
// Called twice, once in displayPreference, the other one in callback
|
||||||
|
verify(mPreference, times(2)).setButton1Enabled(true);
|
||||||
|
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
|
||||||
|
verify(mPreference, times(2))
|
||||||
|
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCallback_onSourceAddFailed_updateButton() {
|
public void testCallback_onSourceAddFailed_updateButton() {
|
||||||
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
|
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
|
||||||
|
@@ -18,6 +18,8 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
|||||||
|
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY;
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -31,6 +33,7 @@ import android.bluetooth.BluetoothLeBroadcastAssistant;
|
|||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
@@ -68,8 +71,9 @@ import java.util.concurrent.Executor;
|
|||||||
ShadowAudioStreamsHelper.class,
|
ShadowAudioStreamsHelper.class,
|
||||||
})
|
})
|
||||||
public class AudioStreamHeaderControllerTest {
|
public class AudioStreamHeaderControllerTest {
|
||||||
|
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
private static final String KEY = "audio_stream_header";
|
private static final String KEY = "audio_stream_header";
|
||||||
private static final int BROADCAST_ID = 1;
|
private static final int BROADCAST_ID = 1;
|
||||||
private static final String BROADCAST_NAME = "broadcast name";
|
private static final String BROADCAST_NAME = "broadcast name";
|
||||||
@@ -81,12 +85,15 @@ public class AudioStreamHeaderControllerTest {
|
|||||||
@Mock private AudioStreamDetailsFragment mFragment;
|
@Mock private AudioStreamDetailsFragment mFragment;
|
||||||
@Mock private LayoutPreference mPreference;
|
@Mock private LayoutPreference mPreference;
|
||||||
@Mock private EntityHeaderController mHeaderController;
|
@Mock private EntityHeaderController mHeaderController;
|
||||||
|
@Mock private BluetoothDevice mBluetoothDevice;
|
||||||
private Lifecycle mLifecycle;
|
private Lifecycle mLifecycle;
|
||||||
private LifecycleOwner mLifecycleOwner;
|
private LifecycleOwner mLifecycleOwner;
|
||||||
private AudioStreamHeaderController mController;
|
private AudioStreamHeaderController mController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
ShadowEntityHeaderController.setUseMock(mHeaderController);
|
ShadowEntityHeaderController.setUseMock(mHeaderController);
|
||||||
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
|
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
|
||||||
@@ -168,6 +175,44 @@ public class AudioStreamHeaderControllerTest {
|
|||||||
verify(mScreen).addPreference(any());
|
verify(mScreen).addPreference(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisplayPreference_sourcePresent_setSummary() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
|
||||||
|
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mBluetoothDevice);
|
||||||
|
when(mBluetoothDevice.getAddress()).thenReturn(address);
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
when(mAudioStreamsHelper.getAllPresentSources())
|
||||||
|
.thenReturn(List.of(mBroadcastReceiveState));
|
||||||
|
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
|
||||||
|
verify(mHeaderController).setLabel(BROADCAST_NAME);
|
||||||
|
verify(mHeaderController).setIcon(any(Drawable.class));
|
||||||
|
verify(mHeaderController)
|
||||||
|
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY));
|
||||||
|
verify(mHeaderController).done(true);
|
||||||
|
verify(mScreen).addPreference(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisplayPreference_sourceNotPresent_setSummary() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
|
when(mAudioStreamsHelper.getAllPresentSources()).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
|
||||||
|
verify(mHeaderController).setLabel(BROADCAST_NAME);
|
||||||
|
verify(mHeaderController).setIcon(any(Drawable.class));
|
||||||
|
verify(mHeaderController).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
|
||||||
|
verify(mHeaderController).done(true);
|
||||||
|
verify(mScreen).addPreference(any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCallback_onSourceRemoved_updateButton() {
|
public void testCallback_onSourceRemoved_updateButton() {
|
||||||
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
|
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
|
||||||
@@ -212,4 +257,25 @@ public class AudioStreamHeaderControllerTest {
|
|||||||
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
|
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
|
||||||
verify(mHeaderController, times(2)).done(true);
|
verify(mHeaderController, times(2)).done(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
when(mAudioStreamsHelper.getAllPresentSources())
|
||||||
|
.thenReturn(List.of(mBroadcastReceiveState));
|
||||||
|
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
|
||||||
|
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mBluetoothDevice);
|
||||||
|
when(mBluetoothDevice.getAddress()).thenReturn(address);
|
||||||
|
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
|
||||||
|
mock(BluetoothDevice.class), /* sourceId= */ 0, mBroadcastReceiveState);
|
||||||
|
|
||||||
|
// Called twice, once in displayPreference, the other one in callback
|
||||||
|
verify(mHeaderController, times(2))
|
||||||
|
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY));
|
||||||
|
verify(mHeaderController, times(2)).done(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -30,6 +32,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
@@ -48,6 +51,8 @@ import org.robolectric.RobolectricTestRunner;
|
|||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public class AudioStreamStateHandlerTest {
|
public class AudioStreamStateHandlerTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
private static final int SUMMARY_RES = 1;
|
private static final int SUMMARY_RES = 1;
|
||||||
private static final String SUMMARY = "summary";
|
private static final String SUMMARY = "summary";
|
||||||
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
|
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
@@ -58,6 +63,7 @@ public class AudioStreamStateHandlerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
mHandler = spy(new AudioStreamStateHandler());
|
mHandler = spy(new AudioStreamStateHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +107,28 @@ public class AudioStreamStateHandlerTest {
|
|||||||
verify(mPreference).setOnPreferenceClickListener(eq(null));
|
verify(mPreference).setOnPreferenceClickListener(eq(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleStateChange_setNewState_sourcePresent() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
|
when(mHandler.getStateEnum())
|
||||||
|
.thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT);
|
||||||
|
when(mPreference.getAudioStreamState())
|
||||||
|
.thenReturn(
|
||||||
|
AudioStreamsProgressCategoryController.AudioStreamState
|
||||||
|
.ADD_SOURCE_BAD_CODE);
|
||||||
|
|
||||||
|
mHandler.handleStateChange(mPreference, mController, mHelper);
|
||||||
|
|
||||||
|
verify(mPreference)
|
||||||
|
.setAudioStreamState(
|
||||||
|
AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT);
|
||||||
|
verify(mHandler).performAction(any(), any(), any());
|
||||||
|
verify(mPreference).setIsConnected(eq(true));
|
||||||
|
verify(mPreference).setSummary(eq(""));
|
||||||
|
verify(mPreference).setOnPreferenceClickListener(eq(null));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandleStateChange_setNewState_newSummary_newListener() {
|
public void testHandleStateChange_setNewState_newSummary_newListener() {
|
||||||
Preference.OnPreferenceClickListener listener =
|
Preference.OnPreferenceClickListener listener =
|
||||||
|
@@ -19,6 +19,8 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
|||||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -37,6 +39,7 @@ import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@@ -74,6 +77,8 @@ import java.util.List;
|
|||||||
})
|
})
|
||||||
public class AudioStreamsHelperTest {
|
public class AudioStreamsHelperTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
private static final int GROUP_ID = 1;
|
private static final int GROUP_ID = 1;
|
||||||
private static final int BROADCAST_ID_1 = 1;
|
private static final int BROADCAST_ID_1 = 1;
|
||||||
private static final int BROADCAST_ID_2 = 2;
|
private static final int BROADCAST_ID_2 = 2;
|
||||||
@@ -86,10 +91,12 @@ public class AudioStreamsHelperTest {
|
|||||||
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
||||||
@Mock private CachedBluetoothDevice mCachedDevice;
|
@Mock private CachedBluetoothDevice mCachedDevice;
|
||||||
@Mock private BluetoothDevice mDevice;
|
@Mock private BluetoothDevice mDevice;
|
||||||
|
@Mock private BluetoothDevice mSourceDevice;
|
||||||
private AudioStreamsHelper mHelper;
|
private AudioStreamsHelper mHelper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||||
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
|
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
|
||||||
@@ -166,6 +173,7 @@ public class AudioStreamsHelperTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void removeSource_memberHasConnectedSource() {
|
public void removeSource_memberHasConnectedSource() {
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
List<BluetoothDevice> devices = new ArrayList<>();
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
var memberDevice = mock(BluetoothDevice.class);
|
var memberDevice = mock(BluetoothDevice.class);
|
||||||
devices.add(mDevice);
|
devices.add(mDevice);
|
||||||
@@ -184,6 +192,8 @@ public class AudioStreamsHelperTest {
|
|||||||
List<Long> bisSyncState = new ArrayList<>();
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
bisSyncState.add(1L);
|
bisSyncState.add(1L);
|
||||||
when(source.getBisSyncState()).thenReturn(bisSyncState);
|
when(source.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
when(source.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
|
||||||
mHelper.removeSource(BROADCAST_ID_2);
|
mHelper.removeSource(BROADCAST_ID_2);
|
||||||
|
|
||||||
@@ -217,6 +227,52 @@ public class AudioStreamsHelperTest {
|
|||||||
assertThat(list.get(0)).isEqualTo(source);
|
assertThat(list.get(0)).isEqualTo(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAllPresentSources_noSource() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
|
devices.add(mDevice);
|
||||||
|
|
||||||
|
String address = "00:00:00:00:00:00";
|
||||||
|
|
||||||
|
when(mAssistant.getAllConnectedDevices()).thenReturn(devices);
|
||||||
|
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
|
||||||
|
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
|
||||||
|
when(mCachedDevice.getDevice()).thenReturn(mDevice);
|
||||||
|
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
|
||||||
|
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
|
||||||
|
when(source.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
|
||||||
|
var list = mHelper.getAllPresentSources();
|
||||||
|
assertThat(list).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAllPresentSources_returnSource() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
List<BluetoothDevice> devices = new ArrayList<>();
|
||||||
|
devices.add(mDevice);
|
||||||
|
|
||||||
|
when(mAssistant.getAllConnectedDevices()).thenReturn(devices);
|
||||||
|
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
|
||||||
|
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
|
||||||
|
when(mCachedDevice.getDevice()).thenReturn(mDevice);
|
||||||
|
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
|
||||||
|
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
|
||||||
|
when(source.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
when(source.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
|
||||||
|
var list = mHelper.getAllPresentSources();
|
||||||
|
assertThat(list).isNotEmpty();
|
||||||
|
assertThat(list.get(0)).isEqualTo(source);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void startMediaService_noDevice_doNothing() {
|
public void startMediaService_noDevice_doNothing() {
|
||||||
mHelper.startMediaService(mContext, BROADCAST_ID_1, BROADCAST_NAME);
|
mHelper.startMediaService(mContext, BROADCAST_ID_1, BROADCAST_NAME);
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
@@ -25,6 +27,7 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@@ -41,14 +44,18 @@ import java.util.List;
|
|||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public class AudioStreamsProgressCategoryCallbackTest {
|
public class AudioStreamsProgressCategoryCallbackTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
@Mock private AudioStreamsProgressCategoryController mController;
|
@Mock private AudioStreamsProgressCategoryController mController;
|
||||||
@Mock private BluetoothDevice mDevice;
|
@Mock private BluetoothDevice mDevice;
|
||||||
@Mock private BluetoothLeBroadcastReceiveState mState;
|
@Mock private BluetoothLeBroadcastReceiveState mState;
|
||||||
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
||||||
|
@Mock private BluetoothDevice mSourceDevice;
|
||||||
private AudioStreamsProgressCategoryCallback mCallback;
|
private AudioStreamsProgressCategoryCallback mCallback;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
mCallback = new AudioStreamsProgressCategoryCallback(mController);
|
mCallback = new AudioStreamsProgressCategoryCallback(mController);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +69,20 @@ public class AudioStreamsProgressCategoryCallbackTest {
|
|||||||
verify(mController).handleSourceConnected(any());
|
verify(mController).handleSourceConnected(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnReceiveStateChanged_sourcePresent() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
when(mState.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
when(mState.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
|
||||||
|
|
||||||
|
verify(mController).handleSourcePresent(any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnReceiveStateChanged_badCode() {
|
public void testOnReceiveStateChanged_badCode() {
|
||||||
when(mState.getPaSyncState())
|
when(mState.getPaSyncState())
|
||||||
|
@@ -20,10 +20,12 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud
|
|||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED;
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SYNCED;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SYNCED;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.UNSET_BROADCAST_ID;
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.UNSET_BROADCAST_ID;
|
||||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
@@ -41,12 +43,14 @@ import static org.robolectric.Shadows.shadowOf;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeAudioContentMetadata;
|
import android.bluetooth.BluetoothLeAudioContentMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -96,6 +100,8 @@ import java.util.List;
|
|||||||
})
|
})
|
||||||
public class AudioStreamsProgressCategoryControllerTest {
|
public class AudioStreamsProgressCategoryControllerTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
private static final String VALID_METADATA =
|
private static final String VALID_METADATA =
|
||||||
"BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
|
"BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
|
||||||
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
|
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
|
||||||
@@ -115,6 +121,7 @@ public class AudioStreamsProgressCategoryControllerTest {
|
|||||||
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
@Mock private BluetoothLeBroadcastMetadata mMetadata;
|
||||||
@Mock private CachedBluetoothDevice mDevice;
|
@Mock private CachedBluetoothDevice mDevice;
|
||||||
@Mock private AudioStreamsProgressCategoryPreference mPreference;
|
@Mock private AudioStreamsProgressCategoryPreference mPreference;
|
||||||
|
@Mock private BluetoothDevice mSourceDevice;
|
||||||
private Lifecycle mLifecycle;
|
private Lifecycle mLifecycle;
|
||||||
private LifecycleOwner mLifecycleOwner;
|
private LifecycleOwner mLifecycleOwner;
|
||||||
private Fragment mFragment;
|
private Fragment mFragment;
|
||||||
@@ -125,6 +132,7 @@ public class AudioStreamsProgressCategoryControllerTest {
|
|||||||
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
||||||
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
|
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
|
||||||
|
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||||
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
||||||
@@ -282,6 +290,29 @@ public class AudioStreamsProgressCategoryControllerTest {
|
|||||||
verify(mController, never()).moveToState(any(), any());
|
verify(mController, never()).moveToState(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnStart_initHasDevice_getPresentSources() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
|
||||||
|
// Setup a device
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
|
||||||
|
|
||||||
|
List<BluetoothLeBroadcastReceiveState> connectedList = new ArrayList<>();
|
||||||
|
// Empty connected device list
|
||||||
|
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(connectedList);
|
||||||
|
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
verify(mAudioStreamsHelper).getAllPresentSources();
|
||||||
|
verify(mLeBroadcastAssistant).startSearchingForSources(any());
|
||||||
|
|
||||||
|
var dialog = ShadowAlertDialog.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNull();
|
||||||
|
|
||||||
|
verify(mController, never()).moveToState(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStart_handleSourceFromQrCode() {
|
public void testOnStart_handleSourceFromQrCode() {
|
||||||
// Setup a device
|
// Setup a device
|
||||||
@@ -764,6 +795,58 @@ public class AudioStreamsProgressCategoryControllerTest {
|
|||||||
assertThat(states.get(1)).isEqualTo(ADD_SOURCE_FAILED);
|
assertThat(states.get(1)).isEqualTo(ADD_SOURCE_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleSourcePresent_updateState() {
|
||||||
|
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
|
||||||
|
String address = "11:22:33:44:55:66";
|
||||||
|
|
||||||
|
// Setup a device
|
||||||
|
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
|
||||||
|
|
||||||
|
// Setup mPreference so it's not null
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
|
||||||
|
// A new source found
|
||||||
|
when(mMetadata.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID);
|
||||||
|
mController.handleSourceFound(mMetadata);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
// The connected source is identified as having a bad code
|
||||||
|
BluetoothLeBroadcastReceiveState receiveState =
|
||||||
|
mock(BluetoothLeBroadcastReceiveState.class);
|
||||||
|
when(receiveState.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID);
|
||||||
|
when(receiveState.getSourceDevice()).thenReturn(mSourceDevice);
|
||||||
|
when(mSourceDevice.getAddress()).thenReturn(address);
|
||||||
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
when(receiveState.getBisSyncState()).thenReturn(bisSyncState);
|
||||||
|
|
||||||
|
// The new found source is identified as failed to connect
|
||||||
|
mController.handleSourcePresent(receiveState);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
ArgumentCaptor<AudioStreamPreference> preference =
|
||||||
|
ArgumentCaptor.forClass(AudioStreamPreference.class);
|
||||||
|
ArgumentCaptor<AudioStreamsProgressCategoryController.AudioStreamState> state =
|
||||||
|
ArgumentCaptor.forClass(
|
||||||
|
AudioStreamsProgressCategoryController.AudioStreamState.class);
|
||||||
|
|
||||||
|
verify(mController, times(2)).moveToState(preference.capture(), state.capture());
|
||||||
|
List<AudioStreamPreference> preferences = preference.getAllValues();
|
||||||
|
assertThat(preferences.size()).isEqualTo(2);
|
||||||
|
List<AudioStreamsProgressCategoryController.AudioStreamState> states = state.getAllValues();
|
||||||
|
assertThat(states.size()).isEqualTo(2);
|
||||||
|
|
||||||
|
// Verify one preference is created with SYNCED
|
||||||
|
assertThat(preferences.get(0).getAudioStreamBroadcastId())
|
||||||
|
.isEqualTo(NEWLY_FOUND_BROADCAST_ID);
|
||||||
|
assertThat(states.get(0)).isEqualTo(SYNCED);
|
||||||
|
|
||||||
|
// Verify the preference is updated to state ADD_SOURCE_FAILED
|
||||||
|
assertThat(preferences.get(1).getAudioStreamBroadcastId())
|
||||||
|
.isEqualTo(NEWLY_FOUND_BROADCAST_ID);
|
||||||
|
assertThat(states.get(1)).isEqualTo(SOURCE_PRESENT);
|
||||||
|
}
|
||||||
|
|
||||||
private static BluetoothLeBroadcastReceiveState createConnectedMock(int id) {
|
private static BluetoothLeBroadcastReceiveState createConnectedMock(int id) {
|
||||||
var connected = mock(BluetoothLeBroadcastReceiveState.class);
|
var connected = mock(BluetoothLeBroadcastReceiveState.class);
|
||||||
List<Long> bisSyncState = new ArrayList<>();
|
List<Long> bisSyncState = new ArrayList<>();
|
||||||
|
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static android.app.settings.SettingsEnums.AUDIO_STREAM_MAIN;
|
||||||
|
|
||||||
|
import static com.android.settings.connecteddevice.audiosharing.audiostreams.SourcePresentState.AUDIO_STREAM_SOURCE_PRESENT_STATE_SUMMARY;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowFragment;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowFragment.class,
|
||||||
|
})
|
||||||
|
public class SourcePresentStateTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
private static final int BROADCAST_ID = 1;
|
||||||
|
private static final String BROADCAST_TITLE = "title";
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@Mock private AudioStreamPreference mPreference;
|
||||||
|
@Mock private AudioStreamsProgressCategoryController mController;
|
||||||
|
@Mock private AudioStreamsHelper mHelper;
|
||||||
|
@Mock private AudioStreamsRepository mRepository;
|
||||||
|
@Mock private AudioStreamsDashboardFragment mFragment;
|
||||||
|
@Mock private FragmentActivity mActivity;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
private SourcePresentState mInstance;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
mInstance = new SourcePresentState();
|
||||||
|
when(mPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID);
|
||||||
|
when(mPreference.getTitle()).thenReturn(BROADCAST_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetInstance() {
|
||||||
|
mInstance = SourcePresentState.getInstance();
|
||||||
|
assertThat(mInstance).isNotNull();
|
||||||
|
assertThat(mInstance).isInstanceOf(SourcePresentState.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSummary() {
|
||||||
|
int summary = mInstance.getSummary();
|
||||||
|
assertThat(summary).isEqualTo(AUDIO_STREAM_SOURCE_PRESENT_STATE_SUMMARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStateEnum() {
|
||||||
|
AudioStreamsProgressCategoryController.AudioStreamState stateEnum =
|
||||||
|
mInstance.getStateEnum();
|
||||||
|
assertThat(stateEnum)
|
||||||
|
.isEqualTo(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetOnClickListener_startSubSettings() {
|
||||||
|
when(mController.getFragment()).thenReturn(mFragment);
|
||||||
|
when(mFragment.getMetricsCategory()).thenReturn(AUDIO_STREAM_MAIN);
|
||||||
|
|
||||||
|
Preference.OnPreferenceClickListener listener = mInstance.getOnClickListener(mController);
|
||||||
|
assertThat(listener).isNotNull();
|
||||||
|
|
||||||
|
// mContext is not an Activity context, calling startActivity() from outside of an Activity
|
||||||
|
// context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this
|
||||||
|
// AndroidRuntimeException.
|
||||||
|
Context activityContext = mock(Context.class);
|
||||||
|
when(mPreference.getContext()).thenReturn(activityContext);
|
||||||
|
|
||||||
|
listener.onPreferenceClick(mPreference);
|
||||||
|
|
||||||
|
ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
|
verify(activityContext).startActivity(argumentCaptor.capture());
|
||||||
|
|
||||||
|
Intent intent = argumentCaptor.getValue();
|
||||||
|
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(AudioStreamDetailsFragment.class.getName());
|
||||||
|
assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
|
||||||
|
.isEqualTo(R.string.audio_streams_detail_page_title);
|
||||||
|
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0))
|
||||||
|
.isEqualTo(AUDIO_STREAM_MAIN);
|
||||||
|
|
||||||
|
Bundle bundle = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
|
||||||
|
assertThat(bundle).isNotNull();
|
||||||
|
assertThat(bundle.getString(AudioStreamDetailsFragment.BROADCAST_NAME_ARG))
|
||||||
|
.isEqualTo(BROADCAST_TITLE);
|
||||||
|
assertThat(bundle.getInt(AudioStreamDetailsFragment.BROADCAST_ID_ARG))
|
||||||
|
.isEqualTo(BROADCAST_ID);
|
||||||
|
}
|
||||||
|
}
|
@@ -59,6 +59,11 @@ public class ShadowAudioStreamsHelper {
|
|||||||
return sMockHelper.getAllConnectedSources();
|
return sMockHelper.getAllConnectedSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
|
||||||
|
return sMockHelper.getAllPresentSources();
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets {@link CachedBluetoothDevice} in sharing or le connected */
|
/** Gets {@link CachedBluetoothDevice} in sharing or le connected */
|
||||||
@Implementation
|
@Implementation
|
||||||
public static Optional<CachedBluetoothDevice> getCachedBluetoothDeviceInSharingOrLeConnected(
|
public static Optional<CachedBluetoothDevice> getCachedBluetoothDeviceInSharingOrLeConnected(
|
||||||
|
@@ -32,7 +32,6 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.UserManager;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.SubscriptionInfo;
|
import android.telephony.SubscriptionInfo;
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
@@ -73,8 +72,6 @@ public class MobileNetworkSummaryControllerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private PreferenceScreen mPreferenceScreen;
|
private PreferenceScreen mPreferenceScreen;
|
||||||
@Mock
|
@Mock
|
||||||
private UserManager mUserManager;
|
|
||||||
@Mock
|
|
||||||
private MobileNetworkRepository mMobileNetworkRepository;
|
private MobileNetworkRepository mMobileNetworkRepository;
|
||||||
@Mock
|
@Mock
|
||||||
private MobileNetworkRepository.MobileNetworkCallback mMobileNetworkCallback;
|
private MobileNetworkRepository.MobileNetworkCallback mMobileNetworkCallback;
|
||||||
@@ -92,7 +89,6 @@ public class MobileNetworkSummaryControllerTest {
|
|||||||
doReturn(mTelephonyManager).when(mContext).getSystemService(TelephonyManager.class);
|
doReturn(mTelephonyManager).when(mContext).getSystemService(TelephonyManager.class);
|
||||||
doReturn(mSubscriptionManager).when(mContext).getSystemService(SubscriptionManager.class);
|
doReturn(mSubscriptionManager).when(mContext).getSystemService(SubscriptionManager.class);
|
||||||
doReturn(mEuiccManager).when(mContext).getSystemService(EuiccManager.class);
|
doReturn(mEuiccManager).when(mContext).getSystemService(EuiccManager.class);
|
||||||
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
|
|
||||||
mMobileNetworkRepository = MobileNetworkRepository.getInstance(mContext);
|
mMobileNetworkRepository = MobileNetworkRepository.getInstance(mContext);
|
||||||
mLifecycleOwner = () -> mLifecycle;
|
mLifecycleOwner = () -> mLifecycle;
|
||||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||||
@@ -118,21 +114,6 @@ public class MobileNetworkSummaryControllerTest {
|
|||||||
SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isAvailable_wifiOnlyMode_notAvailable() {
|
|
||||||
when(mTelephonyManager.isDataCapable()).thenReturn(false);
|
|
||||||
when(mUserManager.isAdminUser()).thenReturn(true);
|
|
||||||
|
|
||||||
assertThat(mController.isAvailable()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isAvailable_secondaryUser_notAvailable() {
|
|
||||||
when(mTelephonyManager.isDataCapable()).thenReturn(true);
|
|
||||||
when(mUserManager.isAdminUser()).thenReturn(false);
|
|
||||||
assertThat(mController.isAvailable()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getSummary_noSubscriptions_returnSummaryCorrectly() {
|
public void getSummary_noSubscriptions_returnSummaryCorrectly() {
|
||||||
mController.displayPreference(mPreferenceScreen);
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.network;
|
|
||||||
|
|
||||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.text.BidiFormatter;
|
|
||||||
import android.util.FeatureFlagUtils;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
|
|
||||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
|
||||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.shadow.api.Shadow;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
@Config(shadows = {
|
|
||||||
ShadowRestrictedLockUtilsInternal.class,
|
|
||||||
ShadowUtils.class,
|
|
||||||
ShadowUserManager.class,
|
|
||||||
})
|
|
||||||
public class TopLevelNetworkEntryPreferenceControllerTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private MobileNetworkPreferenceController mMobileNetworkPreferenceController;;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private TopLevelNetworkEntryPreferenceController mController;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
final ShadowUserManager um = Shadow.extract(
|
|
||||||
RuntimeEnvironment.application.getSystemService(UserManager.class));
|
|
||||||
um.setIsAdminUser(true);
|
|
||||||
|
|
||||||
mController = new TopLevelNetworkEntryPreferenceController(mContext, "test_key");
|
|
||||||
|
|
||||||
ReflectionHelpers.setField(mController, "mMobileNetworkPreferenceController",
|
|
||||||
mMobileNetworkPreferenceController);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
ShadowUtils.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAvailabilityStatus_demoUser_nonLargeScreen_unsupported() {
|
|
||||||
ShadowUtils.setIsDemoUser(true);
|
|
||||||
FeatureFlagUtils.setEnabled(mContext, "settings_support_large_screen", false);
|
|
||||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSummary_hasMobile_shouldReturnMobileSummary() {
|
|
||||||
when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(true);
|
|
||||||
|
|
||||||
assertThat(mController.getSummary()).isEqualTo(BidiFormatter.getInstance().unicodeWrap(
|
|
||||||
mContext.getString(R.string.network_dashboard_summary_mobile)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSummary_noMobile_shouldReturnNoMobileSummary() {
|
|
||||||
when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(false);
|
|
||||||
|
|
||||||
assertThat(mController.getSummary()).isEqualTo(BidiFormatter.getInstance().unicodeWrap(
|
|
||||||
mContext.getString(R.string.network_dashboard_summary_no_mobile)));
|
|
||||||
}
|
|
||||||
}
|
|
@@ -43,7 +43,6 @@ import androidx.preference.PreferenceScreen;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.notification.zen.AbstractZenModePreferenceController.ZenModeConfigWrapper;
|
import com.android.settings.notification.zen.AbstractZenModePreferenceController.ZenModeConfigWrapper;
|
||||||
import com.android.settings.notification.zen.ZenModeBehaviorFooterPreferenceController;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -206,7 +205,7 @@ public class ZenModeBehaviorFooterPreferenceControllerTest {
|
|||||||
ZenRule injectedRule = spy(new ZenRule());
|
ZenRule injectedRule = spy(new ZenRule());
|
||||||
injectedRule.zenMode = ZEN_MODE_ALARMS;
|
injectedRule.zenMode = ZEN_MODE_ALARMS;
|
||||||
injectedRule.component = mock(ComponentName.class);
|
injectedRule.component = mock(ComponentName.class);
|
||||||
when(injectedRule.isAutomaticActive()).thenReturn(true);
|
when(injectedRule.isActive()).thenReturn(true);
|
||||||
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
||||||
injectedAutomaticRules.put("testid", injectedRule);
|
injectedAutomaticRules.put("testid", injectedRule);
|
||||||
|
|
||||||
@@ -226,7 +225,7 @@ public class ZenModeBehaviorFooterPreferenceControllerTest {
|
|||||||
ZenRule injectedRule = spy(new ZenRule());
|
ZenRule injectedRule = spy(new ZenRule());
|
||||||
injectedRule.zenMode = ZEN_MODE_NO_INTERRUPTIONS;
|
injectedRule.zenMode = ZEN_MODE_NO_INTERRUPTIONS;
|
||||||
injectedRule.component = mock(ComponentName.class);
|
injectedRule.component = mock(ComponentName.class);
|
||||||
when(injectedRule.isAutomaticActive()).thenReturn(true);
|
when(injectedRule.isActive()).thenReturn(true);
|
||||||
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
||||||
injectedAutomaticRules.put("testid", injectedRule);
|
injectedAutomaticRules.put("testid", injectedRule);
|
||||||
|
|
||||||
|
@@ -44,7 +44,6 @@ import androidx.preference.Preference;
|
|||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.settings.notification.zen.AbstractZenModePreferenceController.ZenModeConfigWrapper;
|
import com.android.settings.notification.zen.AbstractZenModePreferenceController.ZenModeConfigWrapper;
|
||||||
import com.android.settings.notification.zen.ZenModeSettingsFooterPreferenceController;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -289,7 +288,7 @@ public class ZenModeSettingsFooterPreferenceControllerTest {
|
|||||||
injectedRule.component = mock(ComponentName.class);
|
injectedRule.component = mock(ComponentName.class);
|
||||||
injectedRule.name = nameAndId;
|
injectedRule.name = nameAndId;
|
||||||
injectedRule.conditionId = new Uri.Builder().authority(nameAndId).build(); // unique uri
|
injectedRule.conditionId = new Uri.Builder().authority(nameAndId).build(); // unique uri
|
||||||
when(injectedRule.isAutomaticActive()).thenReturn(isActive);
|
when(injectedRule.isActive()).thenReturn(isActive);
|
||||||
when(mConfigWrapper.isTimeRule(injectedRule.conditionId)).thenReturn(!isApp);
|
when(mConfigWrapper.isTimeRule(injectedRule.conditionId)).thenReturn(!isApp);
|
||||||
if (isApp) {
|
if (isApp) {
|
||||||
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
when(injectedRule.component.getPackageName()).thenReturn(TEST_APP_NAME);
|
||||||
|
@@ -36,11 +36,11 @@ import androidx.preference.PreferenceManager;
|
|||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.settings.core.ObservablePreferenceFragment;
|
||||||
import com.android.settings.users.OwnerInfoSettings;
|
import com.android.settings.users.OwnerInfoSettings;
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
import com.android.settingslib.RestrictedPreference;
|
import com.android.settingslib.RestrictedPreference;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@@ -17,57 +17,37 @@
|
|||||||
package com.android.settings.network
|
package com.android.settings.network
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
|
||||||
import android.os.UserManager
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.network.MobileNetworkListFragment.Companion.SearchIndexProvider
|
||||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
import com.android.settings.network.telephony.SimRepository
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import org.mockito.kotlin.spy
|
|
||||||
import org.mockito.kotlin.stub
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class MobileNetworkListFragmentTest {
|
class MobileNetworkListFragmentTest {
|
||||||
private val mockUserManager = mock<UserManager>()
|
private val mockSimRepository = mock<SimRepository>()
|
||||||
|
|
||||||
private val mockResources = mock<Resources>()
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
|
||||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
|
||||||
on { userManager } doReturn mockUserManager
|
|
||||||
on { resources } doReturn mockResources
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isPageSearchEnabled_adminUser_shouldReturnTrue() {
|
fun isPageSearchEnabled_showMobileNetworkPage_returnTrue() {
|
||||||
mockUserManager.stub {
|
mockSimRepository.stub { on { showMobileNetworkPage() } doReturn true }
|
||||||
on { isAdminUser } doReturn true
|
|
||||||
}
|
|
||||||
mockResources.stub {
|
|
||||||
on { getBoolean(R.bool.config_show_sim_info) } doReturn true
|
|
||||||
}
|
|
||||||
|
|
||||||
val isEnabled =
|
val isEnabled = SearchIndexProvider { mockSimRepository }.isPageSearchEnabled(context)
|
||||||
MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
|
|
||||||
|
|
||||||
assertThat(isEnabled).isTrue()
|
assertThat(isEnabled).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
|
fun isPageSearchEnabled_hideMobileNetworkPage_returnFalse() {
|
||||||
mockUserManager.stub {
|
mockSimRepository.stub { on { showMobileNetworkPage() } doReturn false }
|
||||||
on { isAdminUser } doReturn false
|
|
||||||
}
|
|
||||||
mockResources.stub {
|
|
||||||
on { getBoolean(R.bool.config_show_sim_info) } doReturn true
|
|
||||||
}
|
|
||||||
|
|
||||||
val isEnabled =
|
val isEnabled = SearchIndexProvider { mockSimRepository }.isPageSearchEnabled(context)
|
||||||
MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
|
|
||||||
|
|
||||||
assertThat(isEnabled).isFalse()
|
assertThat(isEnabled).isFalse()
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.network
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.BidiFormatter
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.network.telephony.SimRepository
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class TopLevelNetworkEntryPreferenceControllerTest {
|
||||||
|
|
||||||
|
private val mockSimRepository = mock<SimRepository>()
|
||||||
|
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
|
||||||
|
private var isDemoUser = false
|
||||||
|
private var isEmbeddingActivityEnabled = false
|
||||||
|
|
||||||
|
private var controller =
|
||||||
|
TopLevelNetworkEntryPreferenceController(
|
||||||
|
context = context,
|
||||||
|
preferenceKey = TEST_KEY,
|
||||||
|
simRepository = mockSimRepository,
|
||||||
|
isDemoUser = { isDemoUser },
|
||||||
|
isEmbeddingActivityEnabled = { isEmbeddingActivityEnabled },
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_demoUser_largeScreen_unsupported() {
|
||||||
|
isDemoUser = true
|
||||||
|
isEmbeddingActivityEnabled = true
|
||||||
|
|
||||||
|
val availabilityStatus = controller.availabilityStatus
|
||||||
|
|
||||||
|
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.AVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_demoUser_nonLargeScreen_unsupported() {
|
||||||
|
isDemoUser = true
|
||||||
|
isEmbeddingActivityEnabled = false
|
||||||
|
|
||||||
|
val availabilityStatus = controller.availabilityStatus
|
||||||
|
|
||||||
|
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_hasMobile_shouldReturnMobileSummary() {
|
||||||
|
mockSimRepository.stub { on { showMobileNetworkPage() } doReturn true }
|
||||||
|
|
||||||
|
val summary = controller.summary
|
||||||
|
|
||||||
|
assertThat(summary)
|
||||||
|
.isEqualTo(
|
||||||
|
BidiFormatter.getInstance()
|
||||||
|
.unicodeWrap(context.getString(R.string.network_dashboard_summary_mobile))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getSummary_noMobile_shouldReturnNoMobileSummary() {
|
||||||
|
mockSimRepository.stub { on { showMobileNetworkPage() } doReturn false }
|
||||||
|
|
||||||
|
val summary = controller.summary
|
||||||
|
|
||||||
|
assertThat(summary)
|
||||||
|
.isEqualTo(
|
||||||
|
BidiFormatter.getInstance()
|
||||||
|
.unicodeWrap(context.getString(R.string.network_dashboard_summary_no_mobile))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.UserManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class SimRepositoryTest {
|
||||||
|
|
||||||
|
private val mockUserManager = mock<UserManager>()
|
||||||
|
|
||||||
|
private val mockPackageManager = mock<PackageManager>()
|
||||||
|
|
||||||
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { userManager } doReturn mockUserManager
|
||||||
|
on { packageManager } doReturn mockPackageManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val repository = SimRepository(context)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun showMobileNetworkPage_adminUserAndHasTelephony_returnTrue() {
|
||||||
|
mockUserManager.stub {
|
||||||
|
on { isAdminUser } doReturn true
|
||||||
|
}
|
||||||
|
mockPackageManager.stub {
|
||||||
|
on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true
|
||||||
|
}
|
||||||
|
|
||||||
|
val showMobileNetworkPage = repository.showMobileNetworkPage()
|
||||||
|
|
||||||
|
assertThat(showMobileNetworkPage).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun showMobileNetworkPage_notAdminUser_returnFalse() {
|
||||||
|
mockUserManager.stub {
|
||||||
|
on { isAdminUser } doReturn false
|
||||||
|
}
|
||||||
|
mockPackageManager.stub {
|
||||||
|
on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true
|
||||||
|
}
|
||||||
|
|
||||||
|
val showMobileNetworkPage = repository.showMobileNetworkPage()
|
||||||
|
|
||||||
|
assertThat(showMobileNetworkPage).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun showMobileNetworkPage_noTelephony_returnFalse() {
|
||||||
|
mockUserManager.stub {
|
||||||
|
on { isAdminUser } doReturn true
|
||||||
|
}
|
||||||
|
mockPackageManager.stub {
|
||||||
|
on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn false
|
||||||
|
}
|
||||||
|
|
||||||
|
val showMobileNetworkPage = repository.showMobileNetworkPage()
|
||||||
|
|
||||||
|
assertThat(showMobileNetworkPage).isFalse()
|
||||||
|
}
|
||||||
|
}
|
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.network;
|
|
||||||
|
|
||||||
import static androidx.lifecycle.Lifecycle.Event;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.provider.Settings.Global;
|
|
||||||
import android.telephony.PhoneStateListener;
|
|
||||||
import android.telephony.SubscriptionManager;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
|
||||||
import androidx.lifecycle.LifecycleRegistry;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
|
||||||
import androidx.test.annotation.UiThreadTest;
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
|
||||||
import com.android.settingslib.RestrictedPreference;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class MobileNetworkPreferenceControllerTest {
|
|
||||||
private Context mContext;
|
|
||||||
@Mock
|
|
||||||
private TelephonyManager mTelephonyManager;
|
|
||||||
@Mock
|
|
||||||
private SubscriptionManager mSubscriptionManager;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private UserManager mUserManager;
|
|
||||||
|
|
||||||
private PreferenceManager mPreferenceManager;
|
|
||||||
private PreferenceScreen mScreen;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private LifecycleOwner mLifecycleOwner;
|
|
||||||
private LifecycleRegistry mLifecycleRegistry;
|
|
||||||
private MobileNetworkPreferenceController mController;
|
|
||||||
private Preference mPreference;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
|
||||||
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
|
|
||||||
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
|
|
||||||
when(mSubscriptionManager.createForAllUserProfiles()).thenReturn(mSubscriptionManager);
|
|
||||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
|
||||||
if (Looper.myLooper() == null) {
|
|
||||||
Looper.prepare();
|
|
||||||
}
|
|
||||||
mPreferenceManager = new PreferenceManager(mContext);
|
|
||||||
mScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
|
||||||
mPreference = new Preference(mContext);
|
|
||||||
mPreference.setKey(MobileNetworkPreferenceController.KEY_MOBILE_NETWORK_SETTINGS);
|
|
||||||
|
|
||||||
mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
|
|
||||||
when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void secondaryUser_prefIsNotAvailable() {
|
|
||||||
when(mUserManager.isAdminUser()).thenReturn(false);
|
|
||||||
when(mTelephonyManager.isDataCapable()).thenReturn(true);
|
|
||||||
|
|
||||||
mController = new MobileNetworkPreferenceController(mContext);
|
|
||||||
assertThat(mController.isAvailable()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void wifiOnly_prefIsNotAvailable() {
|
|
||||||
when(mUserManager.isAdminUser()).thenReturn(true);
|
|
||||||
when(mTelephonyManager.isDataCapable()).thenReturn(false);
|
|
||||||
|
|
||||||
mController = new MobileNetworkPreferenceController(mContext);
|
|
||||||
assertThat(mController.isAvailable()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@UiThreadTest
|
|
||||||
public void goThroughLifecycle_isAvailable_shouldListenToServiceChange() {
|
|
||||||
mController = spy(new MobileNetworkPreferenceController(mContext));
|
|
||||||
mLifecycleRegistry.addObserver(mController);
|
|
||||||
doReturn(true).when(mController).isAvailable();
|
|
||||||
|
|
||||||
mLifecycleRegistry.handleLifecycleEvent(Event.ON_START);
|
|
||||||
verify(mController).onStart();
|
|
||||||
verify(mTelephonyManager).registerTelephonyCallback(
|
|
||||||
mContext.getMainExecutor(), mController.mTelephonyCallback);
|
|
||||||
|
|
||||||
mLifecycleRegistry.handleLifecycleEvent(Event.ON_STOP);
|
|
||||||
verify(mController).onStop();
|
|
||||||
verify(mTelephonyManager).unregisterTelephonyCallback(mController.mTelephonyCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@UiThreadTest
|
|
||||||
public void serviceStateChange_shouldUpdatePrefSummary() {
|
|
||||||
final String testCarrierName = "test";
|
|
||||||
|
|
||||||
mController = spy(new MobileNetworkPreferenceController(mContext));
|
|
||||||
mLifecycleRegistry.addObserver(mController);
|
|
||||||
doReturn(true).when(mController).isAvailable();
|
|
||||||
|
|
||||||
mScreen.addPreference(mPreference);
|
|
||||||
|
|
||||||
// Display pref and go through lifecycle to set up listener.
|
|
||||||
mController.displayPreference(mScreen);
|
|
||||||
mLifecycleRegistry.handleLifecycleEvent(Event.ON_START);
|
|
||||||
verify(mController).onStart();
|
|
||||||
verify(mTelephonyManager).registerTelephonyCallback(
|
|
||||||
mContext.getMainExecutor(), mController.mTelephonyCallback);
|
|
||||||
|
|
||||||
doReturn(testCarrierName).when(mController).getSummary();
|
|
||||||
|
|
||||||
mController.mTelephonyCallback.onServiceStateChanged(null);
|
|
||||||
|
|
||||||
// Carrier name should be set.
|
|
||||||
Assert.assertEquals(mPreference.getSummary(), testCarrierName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void airplaneModeTurnedOn_shouldDisablePreference() {
|
|
||||||
Settings.Global.putInt(mContext.getContentResolver(),
|
|
||||||
Global.AIRPLANE_MODE_ON, 1);
|
|
||||||
mController = spy(new MobileNetworkPreferenceController(mContext));
|
|
||||||
final RestrictedPreference mPreference = new RestrictedPreference(mContext);
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
assertThat(mPreference.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void airplaneModeTurnedOffAndNoUserRestriction_shouldEnablePreference() {
|
|
||||||
Settings.Global.putInt(mContext.getContentResolver(),
|
|
||||||
Global.AIRPLANE_MODE_ON, 0);
|
|
||||||
mController = spy(new MobileNetworkPreferenceController(mContext));
|
|
||||||
final RestrictedPreference mPreference = new RestrictedPreference(mContext);
|
|
||||||
mPreference.setDisabledByAdmin(null);
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
assertThat(mPreference.isEnabled()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void airplaneModeTurnedOffAndHasUserRestriction_shouldDisablePreference() {
|
|
||||||
Settings.Global.putInt(mContext.getContentResolver(),
|
|
||||||
Global.AIRPLANE_MODE_ON, 0);
|
|
||||||
mController = spy(new MobileNetworkPreferenceController(mContext));
|
|
||||||
final RestrictedPreference mPreference = new RestrictedPreference(mContext);
|
|
||||||
mPreference.setDisabledByAdmin(EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN);
|
|
||||||
mController.updateState(mPreference);
|
|
||||||
assertThat(mPreference.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user