diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1aac149ecc9..8d410709d11 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4064,6 +4064,20 @@ android:value="true"/> + + + + + + + + + + diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java index 57b1f5c6cbb..598c97394c1 100644 --- a/src/com/android/settings/SettingsLicenseActivity.java +++ b/src/com/android/settings/SettingsLicenseActivity.java @@ -98,7 +98,9 @@ public class SettingsLicenseActivity extends FragmentActivity implements intent.setDataAndType(uri, "text/html"); intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_license_activity_title)); if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags( + Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); } intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setPackage("com.android.htmlviewer"); diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index d020f3386ac..00af9737b39 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -46,6 +46,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageItemInfo; +import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; @@ -133,6 +134,8 @@ import com.android.settings.notification.app.AppNotificationSettings; import com.android.settings.widget.LoadingViewController; import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settings.wifi.ChangeWifiStateDetails; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.applications.AppIconCacheManager; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; @@ -431,7 +434,7 @@ public class ManageApplications extends InstrumentedFragment mResetAppsHelper.onRestoreInstanceState(savedInstanceState); mAppBarLayout = getActivity().findViewById(R.id.app_bar); - disableToolBarScrollableBehavior(); + autoSetCollapsingToolbarLayoutScrolling(); return mRootView; } @@ -798,7 +801,18 @@ public class ManageApplications extends InstrumentedFragment mShowSystem = !mShowSystem; mApplications.rebuild(); } else if (i == R.id.reset_app_preferences) { - mResetAppsHelper.buildResetDialog(); + final boolean appsControlDisallowedBySystem = + RestrictedLockUtilsInternal.hasBaseUserRestriction(getActivity(), + UserManager.DISALLOW_APPS_CONTROL, UserHandle.myUserId()); + final RestrictedLockUtils.EnforcedAdmin appsControlDisallowedAdmin = + RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getActivity(), + UserManager.DISALLOW_APPS_CONTROL, UserHandle.myUserId()); + if (appsControlDisallowedAdmin != null && !appsControlDisallowedBySystem) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent( + getActivity(), appsControlDisallowedAdmin); + } else { + mResetAppsHelper.buildResetDialog(); + } return true; } else if (i == R.id.advanced) { if (mListType == LIST_TYPE_NOTIFICATION) { @@ -893,7 +907,7 @@ public class ManageApplications extends InstrumentedFragment } } - private void disableToolBarScrollableBehavior() { + private void autoSetCollapsingToolbarLayoutScrolling() { final CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); @@ -901,7 +915,8 @@ public class ManageApplications extends InstrumentedFragment new AppBarLayout.Behavior.DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { - return false; + return appBarLayout.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; } }); params.setBehavior(behavior); diff --git a/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java b/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java index b2636a63d30..dd06b679910 100644 --- a/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java +++ b/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java @@ -41,15 +41,18 @@ import com.android.settingslib.media.MediaOutputConstants; * nearby broadcast sources. */ public class BluetoothBroadcastDialog extends InstrumentedDialogFragment { + public static final String KEY_APP_LABEL = "app_label"; public static final String KEY_DEVICE_ADDRESS = BluetoothFindBroadcastsFragment.KEY_DEVICE_ADDRESS; + public static final String KEY_MEDIA_STREAMING = "media_streaming"; private static final String TAG = "BTBroadcastsDialog"; private static final CharSequence UNKNOWN_APP_LABEL = "unknown"; private Context mContext; private CharSequence mCurrentAppLabel = UNKNOWN_APP_LABEL; private String mDeviceAddress; + private boolean mIsMediaStreaming; private LocalBluetoothManager mLocalBluetoothManager; private AlertDialog mAlertDialog; @@ -59,6 +62,7 @@ public class BluetoothBroadcastDialog extends InstrumentedDialogFragment { mContext = getActivity(); mCurrentAppLabel = getActivity().getIntent().getCharSequenceExtra(KEY_APP_LABEL); mDeviceAddress = getActivity().getIntent().getStringExtra(KEY_DEVICE_ADDRESS); + mIsMediaStreaming = getActivity().getIntent().getBooleanExtra(KEY_MEDIA_STREAMING, false); mLocalBluetoothManager = Utils.getLocalBtManager(mContext); setShowsDialog(true); } @@ -75,16 +79,21 @@ public class BluetoothBroadcastDialog extends InstrumentedDialogFragment { mContext.getString(R.string.bluetooth_broadcast_dialog_broadcast_message)); Button broadcastBtn = layout.findViewById(com.android.settingslib.R.id.positive_btn); - if (TextUtils.isEmpty(mCurrentAppLabel)) { - broadcastBtn.setText(mContext.getString(R.string.bluetooth_broadcast_dialog_title)); - } else { - broadcastBtn.setText(mContext.getString( + if (isBroadcastSupported() && mIsMediaStreaming) { + broadcastBtn.setVisibility(View.VISIBLE); + if (TextUtils.isEmpty(mCurrentAppLabel)) { + broadcastBtn.setText(mContext.getString(R.string.bluetooth_broadcast_dialog_title)); + } else { + broadcastBtn.setText(mContext.getString( R.string.bluetooth_broadcast_dialog_broadcast_app, String.valueOf(mCurrentAppLabel))); + } + broadcastBtn.setOnClickListener((view) -> { + launchMediaOutputBroadcastDialog(); + }); + } else { + broadcastBtn.setVisibility(View.GONE); } - broadcastBtn.setOnClickListener((view) -> { - launchMediaOutputBroadcastDialog(); - }); Button findBroadcastBtn = layout.findViewById(com.android.settingslib.R.id.negative_btn); findBroadcastBtn.setText(mContext.getString(R.string.bluetooth_find_broadcast)); @@ -169,4 +178,10 @@ public class BluetoothBroadcastDialog extends InstrumentedDialogFragment { .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME) .setAction(MediaOutputConstants.ACTION_CLOSE_PANEL)); } + + boolean isBroadcastSupported() { + LocalBluetoothLeBroadcast broadcast = + mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile(); + return broadcast != null; + } } diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java index 7f58c0647b2..0253aa6b748 100644 --- a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java +++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java @@ -36,16 +36,15 @@ import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + import com.android.settings.core.InstrumentedFragment; import com.android.settingslib.R; import com.android.settingslib.bluetooth.BluetoothBroadcastUtils; import com.android.settingslib.bluetooth.BluetoothUtils; -import com.android.settingslib.core.lifecycle.ObservableFragment; import com.android.settingslib.qrcode.QrCamera; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; - public class QrCodeScanModeFragment extends InstrumentedFragment implements TextureView.SurfaceTextureListener, QrCamera.ScannerCallback { diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java index 5f15093259f..b4380ab4adc 100644 --- a/src/com/android/settings/core/SettingsBaseActivity.java +++ b/src/com/android/settings/core/SettingsBaseActivity.java @@ -22,6 +22,7 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.text.LineBreakConfig; import android.os.Bundle; @@ -117,7 +118,7 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) .build())); } - disableCollapsingToolbarLayoutScrollingBehavior(); + autoSetCollapsingToolbarLayoutScrolling(); } else { super.setContentView(R.layout.settings_base_layout); } @@ -252,7 +253,7 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa return false; } - private void disableCollapsingToolbarLayoutScrollingBehavior() { + private void autoSetCollapsingToolbarLayoutScrolling() { if (mAppBarLayout == null) { return; } @@ -263,7 +264,9 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa new AppBarLayout.Behavior.DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { - return false; + // Header can be scrolling while device in landscape mode. + return appBarLayout.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; } }); params.setBehavior(behavior); diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java index 6ce9f6f9ae8..f801cb9ec77 100644 --- a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java +++ b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java @@ -16,6 +16,7 @@ package com.android.settings.datetime.timezone; +import android.content.res.Configuration; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@ -97,7 +98,7 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment LinearLayoutManager.VERTICAL, /* reverseLayout */ false)); mRecyclerView.setAdapter(mAdapter); mAppBarLayout = getActivity().findViewById(R.id.app_bar); - disableToolBarScrollableBehavior(); + autoSetCollapsingToolbarLayoutScrolling(); // Initialize TimeZoneDataLoader only when mRecyclerView is ready to avoid race // during onDateLoaderReady callback. @@ -193,7 +194,7 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment void onListItemClick(T item); } - private void disableToolBarScrollableBehavior() { + private void autoSetCollapsingToolbarLayoutScrolling() { CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); @@ -201,7 +202,8 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment new AppBarLayout.Behavior.DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { - return false; + return appBarLayout.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; } }); params.setBehavior(behavior); diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java index 0483aea861f..343ed24e0a1 100644 --- a/src/com/android/settings/development/qstile/DevelopmentTiles.java +++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java @@ -476,4 +476,77 @@ public abstract class DevelopmentTiles extends TileService { Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); } } + + /** + * Tile to enable desktop mode + */ + public static class DesktopMode extends DevelopmentTiles { + + private static final int SETTING_VALUE_ON = 1; + private static final int SETTING_VALUE_OFF = 0; + private Context mContext; + + @Override + public void onCreate() { + super.onCreate(); + mContext = getApplicationContext(); + } + + @Override + protected boolean isEnabled() { + return Settings.System.getInt(mContext.getContentResolver(), + Settings.System.DESKTOP_MODE, SETTING_VALUE_OFF) == SETTING_VALUE_ON; + } + + private boolean isDesktopModeFlagEnabled() { + return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false); + } + + private boolean isFreeformFlagEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, SETTING_VALUE_OFF) + == SETTING_VALUE_ON; + } + + private boolean isCaptionOnShellEnabled() { + return SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false); + } + + @Override + protected void setIsEnabled(boolean isEnabled) { + if (isEnabled) { + // Check that all required features are enabled + if (!isDesktopModeFlagEnabled()) { + closeShade(); + showMessage( + "Enable 'Desktop Windowing Proto 1' from the Flag Flipper app"); + return; + } + if (!isCaptionOnShellEnabled()) { + closeShade(); + showMessage("Enable 'Captions in Shell' from the Flag Flipper app"); + return; + } + if (!isFreeformFlagEnabled()) { + closeShade(); + showMessage( + "Enable freeform windows from developer settings"); + return; + } + } + + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.DESKTOP_MODE, + isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + closeShade(); + } + + private void closeShade() { + sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + private void showMessage(String message) { + Toast.makeText(mContext, message, Toast.LENGTH_LONG).show(); + } + } } diff --git a/src/com/android/settings/notification/ConfigureNotificationSettings.java b/src/com/android/settings/notification/ConfigureNotificationSettings.java index f888ea7c8e0..19222612f3a 100644 --- a/src/com/android/settings/notification/ConfigureNotificationSettings.java +++ b/src/com/android/settings/notification/ConfigureNotificationSettings.java @@ -107,7 +107,6 @@ public class ConfigureNotificationSettings extends DashboardFragment implements mNotificationAssistantPreferenceController = use(NotificationAssistantPreferenceController.class); mNotificationAssistantPreferenceController.setFragment(this); - mNotificationAssistantPreferenceController.setBackend(new NotificationBackend()); } private static List buildPreferenceControllers(Context context, diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index 05c8e033930..2466b7747a3 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -29,6 +29,7 @@ import androidx.core.graphics.drawable.IconCompat; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.bluetooth.BluetoothBroadcastDialog; @@ -90,13 +91,16 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont return R.drawable.ic_media_stream_off; } - private boolean isSupportEndItem() { - return getWorker() != null - && getWorker().getActiveLocalMediaController() != null - && isConnectedBLEDevice(); + @VisibleForTesting + boolean isSupportEndItem() { + return isConnectedBLEDevice(); } private boolean isConnectedBLEDevice() { + if (getWorker() == null) { + Log.d(TAG, "The Worker is null"); + return false; + } mMediaDevice = getWorker().getCurrentConnectedMediaDevice(); if (mMediaDevice != null) { return mMediaDevice.isBLEDevice(); @@ -133,6 +137,8 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont Utils.getApplicationLabel(mContext, getWorker().getPackageName())); intent.putExtra(BluetoothBroadcastDialog.KEY_DEVICE_ADDRESS, bluetoothDevice.getAddress()); + intent.putExtra(BluetoothBroadcastDialog.KEY_MEDIA_STREAMING, getWorker() != null + && getWorker().getActiveLocalMediaController() != null); pi = PendingIntent.getActivity(context, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); diff --git a/src/com/android/settings/notification/NotificationAssistantPreferenceController.java b/src/com/android/settings/notification/NotificationAssistantPreferenceController.java index c25a7d55723..e8a66b6ba7f 100644 --- a/src/com/android/settings/notification/NotificationAssistantPreferenceController.java +++ b/src/com/android/settings/notification/NotificationAssistantPreferenceController.java @@ -43,6 +43,7 @@ public class NotificationAssistantPreferenceController extends TogglePreferenceC public NotificationAssistantPreferenceController(Context context) { super(context, KEY_NAS); mUserManager = UserManager.get(context); + mNotificationBackend = new NotificationBackend(); } @Override @@ -100,4 +101,9 @@ public class NotificationAssistantPreferenceController extends TogglePreferenceC void setBackend(NotificationBackend backend) { mNotificationBackend = backend; } + + @Override + public boolean isSliceable() { + return (mFragment != null && mFragment instanceof ConfigureNotificationSettings); + } } diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index 199d584cc5c..c4a3159e001 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -69,6 +69,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.ImeAwareEditText; import android.widget.TextView; @@ -214,6 +215,7 @@ public class ChooseLockPassword extends SettingsActivity { ThemeHelper.trySetDynamicColor(this); super.onCreate(savedInstanceState); findViewById(R.id.content_parent).setFitsSystemWindows(false); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } public static class ChooseLockPasswordFragment extends InstrumentedFragment diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index 65582627d9a..c39ef661a0f 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -41,6 +41,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.TextView; import androidx.fragment.app.Fragment; @@ -175,6 +176,7 @@ public class ChooseLockPattern extends SettingsActivity { ThemeHelper.trySetDynamicColor(this); super.onCreate(savedInstanceState); findViewById(R.id.content_parent).setFitsSystemWindows(false); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } @Override diff --git a/src/com/android/settings/privacy/WorkPolicyInfoPreferenceController.java b/src/com/android/settings/privacy/WorkPolicyInfoPreferenceController.java index 55ba0646af5..2d1d9e18564 100644 --- a/src/com/android/settings/privacy/WorkPolicyInfoPreferenceController.java +++ b/src/com/android/settings/privacy/WorkPolicyInfoPreferenceController.java @@ -24,6 +24,8 @@ import androidx.preference.Preference; import com.android.settings.core.BasePreferenceController; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.safetycenter.SafetyCenterManagerWrapper; + public class WorkPolicyInfoPreferenceController extends BasePreferenceController { @@ -37,7 +39,12 @@ public class WorkPolicyInfoPreferenceController extends BasePreferenceController @Override public int getAvailabilityStatus() { - return mEnterpriseProvider.hasWorkPolicyInfo() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + // Your Work policy info will be shown in the Safety Center. + // No need to show it in the privacy settings. + return !SafetyCenterManagerWrapper.get().isEnabled(mContext) + && mEnterpriseProvider.hasWorkPolicyInfo() + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; } @Override diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java index c91f16c161d..4c5a4bfe3de 100644 --- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java +++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java @@ -19,6 +19,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; import android.app.Activity; import android.app.AlertDialog; @@ -49,7 +50,6 @@ import android.os.Handler; import android.provider.Telephony.CarrierId; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; @@ -690,34 +690,17 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle // Checks if the SIM subscription is active. final List activeSubscriptionInfos = mContext .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList(); - final int defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); if (activeSubscriptionInfos != null) { - CharSequence firstCarrierIdMatchedDisplayName = null; - for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfos) { - final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( - subscriptionInfo, mContext); - if (firstCarrierIdMatchedDisplayName == null - && config.carrierId == subscriptionInfo.getCarrierId()) { - firstCarrierIdMatchedDisplayName = displayName; - } - - // When it's UNKNOWN_CARRIER_ID or matched with configured CarrierId, - // devices connects it with the SIM subscription of defaultDataSubscriptionId. - if (defaultDataSubscriptionId == subscriptionInfo.getSubscriptionId() - && (config.carrierId == subscriptionInfo.getCarrierId() - || config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID)) { - mEapSimSubscriptionPref.setSummary(displayName); - return; - } - } - - if (firstCarrierIdMatchedDisplayName != null) { - mEapSimSubscriptionPref.setSummary(firstCarrierIdMatchedDisplayName); + SubscriptionInfo info = fineSubscriptionInfo(config.carrierId, activeSubscriptionInfos, + SubscriptionManager.getDefaultDataSubscriptionId()); + if (info != null) { + mEapSimSubscriptionPref.setSummary( + SubscriptionUtil.getUniqueSubscriptionDisplayName(info, mContext)); return; } } - if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { + if (config.carrierId == UNKNOWN_CARRIER_ID) { mEapSimSubscriptionPref.setSummary(R.string.wifi_no_related_sim_card); return; } @@ -736,6 +719,25 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle null /* orderBy */); } + @VisibleForTesting + SubscriptionInfo fineSubscriptionInfo(int carrierId, + List activeSubscriptionInfos, int defaultDataSubscriptionId) { + SubscriptionInfo firstMatchedInfo = null; + for (SubscriptionInfo info : activeSubscriptionInfos) { + // When it's UNKNOWN_CARRIER_ID or matched with configured CarrierId, + // devices connects it with the SIM subscription of defaultDataSubscriptionId. + if (defaultDataSubscriptionId == info.getSubscriptionId() + && (carrierId == info.getCarrierId() || carrierId == UNKNOWN_CARRIER_ID)) { + return info; + } + + if (firstMatchedInfo == null && carrierId == info.getCarrierId()) { + firstMatchedInfo = info; + } + } + return firstMatchedInfo; + } + private void refreshMacAddress() { final String macAddress = mWifiEntry.getMacAddress(); if (TextUtils.isEmpty(macAddress)) { diff --git a/tests/robotests/src/com/android/settings/SettingsLicenseActivityTest.java b/tests/robotests/src/com/android/settings/SettingsLicenseActivityTest.java index e98ca311598..6b00255f3af 100644 --- a/tests/robotests/src/com/android/settings/SettingsLicenseActivityTest.java +++ b/tests/robotests/src/com/android/settings/SettingsLicenseActivityTest.java @@ -92,7 +92,8 @@ public class SettingsLicenseActivityTest { "text/html"); intent.putExtra(Intent.EXTRA_TITLE, mActivity.getString( R.string.settings_license_activity_title)); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags( + Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setPackage("com.android.htmlviewer"); diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java index 7cfcaffb97d..56e83bb90ae 100644 --- a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java @@ -16,29 +16,71 @@ package com.android.settings.notification; +import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; import android.media.AudioManager; +import android.media.session.MediaController; +import android.net.Uri; + +import androidx.slice.builders.SliceAction; + +import com.android.settings.media.MediaOutputIndicatorWorker; +import com.android.settings.slices.SliceBackgroundWorker; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.media.BluetoothMediaDevice; +import com.android.settingslib.media.MediaDevice; +import com.android.settingslib.media.MediaOutputConstants; 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.annotation.Implementation; +import org.robolectric.annotation.Implements; @RunWith(RobolectricTestRunner.class) +@Config(shadows = MediaVolumePreferenceControllerTest.ShadowSliceBackgroundWorker.class) public class MediaVolumePreferenceControllerTest { + private static final String ACTION_LAUNCH_BROADCAST_DIALOG = + "android.settings.MEDIA_BROADCAST_DIALOG"; + private static MediaOutputIndicatorWorker sMediaOutputIndicatorWorker; + private MediaVolumePreferenceController mController; private Context mContext; + @Mock + private MediaController mMediaController; + @Mock + private MediaDevice mDevice1; + @Mock + private MediaDevice mDevice2; + @Before public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; mController = new MediaVolumePreferenceController(mContext); + sMediaOutputIndicatorWorker = spy( + new MediaOutputIndicatorWorker(mContext, VOLUME_MEDIA_URI)); + when(mDevice1.isBLEDevice()).thenReturn(true); + when(mDevice2.isBLEDevice()).thenReturn(false); } @Test @@ -68,4 +110,83 @@ public class MediaVolumePreferenceControllerTest { public void isPublicSlice_returnTrue() { assertThat(mController.isPublicSlice()).isTrue(); } + + @Test + public void isSupportEndItem_withBleDevice_returnsTrue() { + doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice(); + + assertThat(mController.isSupportEndItem()).isTrue(); + } + + @Test + public void isSupportEndItem_withNonBleDevice_returnsFalse() { + doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice(); + + assertThat(mController.isSupportEndItem()).isFalse(); + } + + @Test + public void getSliceEndItem_NotSupportEndItem_getsNullSliceAction() { + doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice(); + + final SliceAction sliceAction = mController.getSliceEndItem(mContext); + + assertThat(sliceAction).isNull(); + } + + @Test + public void getSliceEndItem_deviceIsBroadcasting_getsBroadcastIntent() { + doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice(); + doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting(); + doReturn(mMediaController).when(sMediaOutputIndicatorWorker) + .getActiveLocalMediaController(); + + final SliceAction sliceAction = mController.getSliceEndItem(mContext); + + final PendingIntent endItemPendingIntent = sliceAction.getAction(); + final PendingIntent expectedToggleIntent = getBroadcastIntent( + MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + assertThat(endItemPendingIntent).isEqualTo(expectedToggleIntent); + } + + @Test + public void getSliceEndItem_deviceIsNotBroadcasting_getsActivityIntent() { + final MediaDevice device = mock(BluetoothMediaDevice.class); + final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice); + when(device.isBLEDevice()).thenReturn(true); + doReturn(device).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice(); + doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting(); + doReturn(mMediaController).when(sMediaOutputIndicatorWorker) + .getActiveLocalMediaController(); + + final SliceAction sliceAction = mController.getSliceEndItem(mContext); + + final PendingIntent endItemPendingIntent = sliceAction.getAction(); + final PendingIntent expectedPendingIntent = + getActivityIntent(ACTION_LAUNCH_BROADCAST_DIALOG); + assertThat(endItemPendingIntent).isEqualTo(expectedPendingIntent); + } + + @Implements(SliceBackgroundWorker.class) + public static class ShadowSliceBackgroundWorker { + + @Implementation + public static SliceBackgroundWorker getInstance(Uri uri) { + return sMediaOutputIndicatorWorker; + } + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + } + + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + } } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java index 3c50c6bf2f4..43a49a1a1b3 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java @@ -27,6 +27,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY; import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS; @@ -158,6 +159,21 @@ public class ChooseLockPasswordTest { .isNotNull(); } + @Test + public void activity_shouldHaveSecureFlag() { + PasswordPolicy policy = new PasswordPolicy(); + policy.quality = PASSWORD_QUALITY_ALPHABETIC; + policy.length = 10; + + Intent intent = createIntentForPasswordValidation( + /* minMetrics */ policy.getMinMetrics(), + /* minComplexity= */ PASSWORD_COMPLEXITY_NONE, + /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC); + ChooseLockPassword activity = buildChooseLockPasswordActivity(intent); + final int flags = activity.getWindow().getAttributes().flags; + assertThat(flags & FLAG_SECURE).isEqualTo(FLAG_SECURE); + } + @Test public void processAndValidatePasswordRequirements_noMinPasswordComplexity() { PasswordPolicy policy = new PasswordPolicy(); diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java index f5cc39435ad..1fc10fc75f2 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java @@ -16,6 +16,8 @@ package com.android.settings.password; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -107,6 +109,14 @@ public class ChooseLockPatternTest { assertThat(iconView.getVisibility()).isEqualTo(View.GONE); } + @Test + public void activity_shouldHaveSecureFlag() { + final ChooseLockPattern activity = Robolectric.buildActivity( + ChooseLockPattern.class, new IntentBuilder(application).build()).setup().get(); + final int flags = activity.getWindow().getAttributes().flags; + assertThat(flags & FLAG_SECURE).isEqualTo(FLAG_SECURE); + } + private ChooseLockPattern createActivity(boolean addFingerprintExtra) { return Robolectric.buildActivity( ChooseLockPattern.class, diff --git a/tests/robotests/src/com/android/settings/privacy/WorkPolicyInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/WorkPolicyInfoPreferenceControllerTest.java index 82444aade07..a983228255e 100644 --- a/tests/robotests/src/com/android/settings/privacy/WorkPolicyInfoPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/privacy/WorkPolicyInfoPreferenceControllerTest.java @@ -21,6 +21,7 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,6 +31,7 @@ import android.content.Context; import androidx.preference.Preference; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; +import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; @@ -52,6 +54,7 @@ public class WorkPolicyInfoPreferenceControllerTest { mContext = RuntimeEnvironment.application; mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mEnterpriseProvider = mFakeFeatureFactory.getEnterprisePrivacyFeatureProvider(mContext); + SafetyCenterManagerWrapper.sInstance = mock(SafetyCenterManagerWrapper.class); } @Test @@ -72,6 +75,15 @@ public class WorkPolicyInfoPreferenceControllerTest { assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE); } + @Test + public void getAvailabilityStatus_safetyCenterEnabled_shouldReturnUnsupported() { + when(SafetyCenterManagerWrapper.get().isEnabled(mContext)).thenReturn(true); + WorkPolicyInfoPreferenceController controller = + new WorkPolicyInfoPreferenceController(mContext, "test_key"); + + assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + @Test public void handlePreferenceTreeClick_nonMatchingKey_shouldDoNothing() { when(mEnterpriseProvider.hasWorkPolicyInfo()).thenReturn(true); diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java index b0a50c81fa8..c86a0230351 100644 --- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java @@ -59,6 +59,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import android.provider.Settings; +import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.view.View; import android.view.View.OnClickListener; @@ -108,6 +109,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; // TODO(b/143326832): Should add test cases for connect button. @@ -1791,4 +1793,74 @@ public class WifiDetailPreferenceController2Test { return pref; } + + @Test + public void fineSubscriptionInfo_noMatchedCarrierId_returnNull() { + setUpSpyController(); + SubscriptionInfo sub1 = mockSubscriptionInfo(1, "sim1", 1111); + SubscriptionInfo sub2 = mockSubscriptionInfo(2, "sim2", 2222); + List activeSubInfos = Arrays.asList(sub1, sub2); + + SubscriptionInfo info = mController.fineSubscriptionInfo(3333, activeSubInfos, 1); + + assertThat(info).isNull(); + + info = mController.fineSubscriptionInfo(3333, activeSubInfos, 2); + + assertThat(info).isNull(); + } + + @Test + public void fineSubscriptionInfo_diffCarrierId_returnMatchedOne() { + setUpSpyController(); + SubscriptionInfo sub1 = mockSubscriptionInfo(1, "sim1", 1111); + SubscriptionInfo sub2 = mockSubscriptionInfo(2, "sim2", 2222); + List activeSubInfos = Arrays.asList(sub1, sub2); + + SubscriptionInfo info = mController.fineSubscriptionInfo(1111, activeSubInfos, 1); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim1"); + + info = mController.fineSubscriptionInfo(1111, activeSubInfos, 2); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim1"); + + info = mController.fineSubscriptionInfo(2222, activeSubInfos, 1); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim2"); + + info = mController.fineSubscriptionInfo(2222, activeSubInfos, 2); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim2"); + } + + @Test + public void fineSubscriptionInfo_sameCarrierId_returnDefaultDataOne() { + setUpSpyController(); + SubscriptionInfo sub1 = mockSubscriptionInfo(1, "sim1", 1111); + SubscriptionInfo sub2 = mockSubscriptionInfo(2, "sim2", 1111); + List activeSubInfos = Arrays.asList(sub1, sub2); + + SubscriptionInfo info = mController.fineSubscriptionInfo(1111, activeSubInfos, 1); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim1"); + + info = mController.fineSubscriptionInfo(1111, activeSubInfos, 2); + + assertThat(info).isNotNull(); + assertThat(info.getDisplayName().toString()).isEqualTo("sim2"); + } + + private SubscriptionInfo mockSubscriptionInfo(int subId, String displayName, int carrierId) { + SubscriptionInfo info = mock(SubscriptionInfo.class); + when(info.getSubscriptionId()).thenReturn(subId); + when(info.getDisplayName()).thenReturn(displayName); + when(info.getCarrierId()).thenReturn(carrierId); + return info; + } }