diff --git a/res/drawable/volume_dialog_button_background_outline.xml b/res/drawable/volume_dialog_button_background_outline.xml
index 78f3fcf3c5b..efbaec63bd5 100644
--- a/res/drawable/volume_dialog_button_background_outline.xml
+++ b/res/drawable/volume_dialog_button_background_outline.xml
@@ -14,18 +14,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
-
-
-
-
-
\ No newline at end of file
+ android:insetTop="6dp"
+ android:insetBottom="6dp">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/volume_dialog_button_background_solid.xml b/res/drawable/volume_dialog_button_background_solid.xml
index 1fa8f20cf31..697131db247 100644
--- a/res/drawable/volume_dialog_button_background_solid.xml
+++ b/res/drawable/volume_dialog_button_background_solid.xml
@@ -15,17 +15,20 @@
limitations under the License.
-->
-
-
-
-
-
-
\ No newline at end of file
+ android:insetTop="6dp"
+ android:insetBottom="6dp">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/panel_layout.xml b/res/layout/panel_layout.xml
index 2b9daebf85d..f154abc40cb 100644
--- a/res/layout/panel_layout.xml
+++ b/res/layout/panel_layout.xml
@@ -117,7 +117,7 @@
android:id="@+id/see_more"
style="@style/PanelOptionRoundedOutlinedButton"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:minWidth="0dp"
android:text="@string/settings_button"/>
@@ -130,7 +130,7 @@
android:id="@+id/done"
style="@style/PanelOptionRoundedSolidButton"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:minWidth="0dp"
android:text="@string/done"/>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index b2de0041b1b..8ee4ebac502 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -117,6 +117,7 @@ import com.android.settingslib.widget.AdaptiveIcon;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
public final class Utils extends com.android.settingslib.Utils {
@@ -589,7 +590,9 @@ public final class Utils extends com.android.settingslib.Utils {
return inflater.inflate(resId, parent, false);
}
- public static ArraySet getHandledDomains(PackageManager pm, String packageName) {
+ /** Gets all the domains that the given package could handled. */
+ @NonNull
+ public static Set getHandledDomains(PackageManager pm, String packageName) {
final List iviList =
pm.getIntentFilterVerifications(packageName);
final List filters = pm.getAllIntentFilters(packageName);
@@ -597,9 +600,7 @@ public final class Utils extends com.android.settingslib.Utils {
final ArraySet result = new ArraySet<>();
if (iviList != null && iviList.size() > 0) {
for (IntentFilterVerificationInfo ivi : iviList) {
- for (String host : ivi.getDomains()) {
- result.add(host);
- }
+ result.addAll(ivi.getDomains());
}
}
if (filters != null && filters.size() > 0) {
diff --git a/src/com/android/settings/applications/OpenSupportedLinks.java b/src/com/android/settings/applications/OpenSupportedLinks.java
index 4f5f2a8001c..c4e478cefcb 100644
--- a/src/com/android/settings/applications/OpenSupportedLinks.java
+++ b/src/com/android/settings/applications/OpenSupportedLinks.java
@@ -23,7 +23,6 @@ import android.app.settings.SettingsEnums;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import android.view.View;
@@ -36,6 +35,8 @@ import com.android.settings.Utils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
+import java.util.Set;
+
/**
* Display the Open Supported Links page. Allow users choose what kind supported links they need.
*/
@@ -195,7 +196,7 @@ public class OpenSupportedLinks extends AppInfoWithHeader implements
@VisibleForTesting
void addLinksToFooter(FooterPreference footer) {
- final ArraySet result = Utils.getHandledDomains(mPackageManager, mPackageName);
+ final Set result = Utils.getHandledDomains(mPackageManager, mPackageName);
if (result.isEmpty()) {
Log.w(TAG, "Can't find any app links.");
return;
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 49f676ed508..19f3a46aaee 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -22,6 +22,7 @@ import android.os.AsyncTask;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -144,7 +145,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
} else {
Log.i(TAG, "Launch : " + mPackageName + " with package name");
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
- mPackageName);
+ mPackageName, UserHandle.CURRENT);
}
return true;
}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 80b49312475..b678e24b5e2 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -686,7 +686,8 @@ public class ManageApplications extends InstrumentedFragment
break;
case LIST_TYPE_BATTERY_OPTIMIZATION:
AdvancedPowerUsageDetail.startBatteryDetailPage(
- getActivity(), this, mCurrentPkgName);
+ getActivity(), this, mCurrentPkgName,
+ UserHandle.getUserHandleForUid(mCurrentUid));
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
index a62bbe10e0c..8542fcd2461 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
@@ -24,7 +24,6 @@ import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.PowerManager;
import android.os.UserManager;
import android.util.Log;
@@ -126,15 +125,8 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver {
// Create an intent triggered by clicking on the
// "Clear All Notifications" button
- String bluetoothName;
- try {
- bluetoothName = Utils.findBluetoothPackageName(context);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- return;
- }
Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
- deleteIntent.setPackage(bluetoothName);
+ deleteIntent.setPackage("com.android.bluetooth");
deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
BluetoothDevice.CONNECTION_ACCESS_NO);
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
old mode 100644
new mode 100755
index 24fe4e151ff..ca8f9d39a8c
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -16,18 +16,11 @@
package com.android.settings.bluetooth;
-import static android.os.Process.BLUETOOTH_UID;
-
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
@@ -196,48 +189,4 @@ public final class Utils {
}
return false;
}
-
- /**
- * Returns the Bluetooth Package name
- */
- public static String findBluetoothPackageName(Context context)
- throws NameNotFoundException {
- // this activity will always be in the package where the rest of Bluetooth lives
- String sentinelActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity";
- PackageManager packageManager = context.createContextAsUser(UserHandle.SYSTEM, 0)
- .getPackageManager();
- String[] allPackages = packageManager.getPackagesForUid(BLUETOOTH_UID);
- String matchedPackage = null;
- for (String candidatePackage : allPackages) {
- PackageInfo packageInfo;
- try {
- packageInfo =
- packageManager.getPackageInfo(
- candidatePackage,
- PackageManager.GET_ACTIVITIES
- | PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- } catch (NameNotFoundException e) {
- // rethrow
- throw e;
- }
- if (packageInfo.activities == null) {
- continue;
- }
- for (ActivityInfo activity : packageInfo.activities) {
- if (sentinelActivity.equals(activity.name)) {
- if (matchedPackage == null) {
- matchedPackage = candidatePackage;
- } else {
- throw new NameNotFoundException("multiple main bluetooth packages found");
- }
- }
- }
- }
- if (matchedPackage != null) {
- return matchedPackage;
- }
- throw new NameNotFoundException("Could not find main bluetooth package");
- }
}
diff --git a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
index f1677f29509..bd8169a80fe 100644
--- a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
@@ -16,8 +16,9 @@
package com.android.settings.development;
-import android.bluetooth.BluetoothManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.SystemProperties;
import androidx.annotation.VisibleForTesting;
@@ -41,15 +42,18 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceController extends
private int mDefaultMaxConnectedAudioDevices = 0;
- private final BluetoothManager mBluetoothManager;
-
public BluetoothMaxConnectedAudioDevicesPreferenceController(Context context) {
super(context);
- mBluetoothManager = context.getSystemService(BluetoothManager.class);
-
- mDefaultMaxConnectedAudioDevices =
- mBluetoothManager.getAdapter().getMaxConnectedAudioDevices();
+ try {
+ Resources res = context.getPackageManager().getResourcesForApplication(
+ "com.android.bluetooth");
+ mDefaultMaxConnectedAudioDevices = res.getInteger(res.getIdentifier(
+ "config_bluetooth_max_connected_audio_devices",
+ "integer", "com.android.bluetooth"));
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
}
@Override
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index c08a1b8985e..9705154d7b3 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -219,7 +219,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
* Start packageName's battery detail page.
*/
public static void startBatteryDetailPage(
- Activity caller, Instrumentable instrumentable, String packageName) {
+ Activity caller, Instrumentable instrumentable, String packageName,
+ UserHandle userHandle) {
final Bundle args = new Bundle(3);
final PackageManager packageManager = caller.getPackageManager();
args.putString(EXTRA_PACKAGE_NAME, packageName);
@@ -235,6 +236,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
.setTitleRes(R.string.battery_details_title)
.setArguments(args)
.setSourceMetricsCategory(instrumentable.getMetricsCategory())
+ .setUserHandle(userHandle)
.launch();
}
@@ -475,16 +477,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
private CharSequence getAppActiveTime(Bundle bundle) {
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
- final int consumedPower = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
- final int uid = bundle.getInt(EXTRA_UID, 0);
final String slotTime = bundle.getString(EXTRA_SLOT_TIME, null);
final long totalTimeMs = foregroundTimeMs + backgroundTimeMs;
final CharSequence usageTimeSummary;
if (totalTimeMs == 0) {
- usageTimeSummary = getText(
- consumedPower > 0 ? R.string.battery_usage_without_time
- : R.string.battery_not_usage);
+ usageTimeSummary = getText(R.string.battery_usage_without_time);
} else if (slotTime == null) {
// Shows summary text with last full charge if slot time is null.
usageTimeSummary = getAppFullChargeActiveSummary(
diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
index 322bb6c229b..112debca647 100644
--- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java
+++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.notification;
+import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -29,26 +30,32 @@ import android.os.Looper;
import android.os.Message;
import android.os.ServiceManager;
import android.os.Vibrator;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.Log;
import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.Objects;
+import java.util.Set;
/**
- * Update notification volume icon in Settings in response to user adjusting volume
+ * Update notification volume icon in Settings in response to user adjusting volume.
*/
public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController {
private static final String TAG = "NotificationVolumePreferenceController";
private static final String KEY_NOTIFICATION_VOLUME = "notification_volume";
+ private static final boolean CONFIG_DEFAULT_VAL = false;
+ private boolean mSeparateNotification;
private Vibrator mVibrator;
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
@@ -56,39 +63,74 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere
private final RingReceiver mReceiver = new RingReceiver();
private final H mHandler = new H();
private INotificationManager mNoMan;
-
-
private int mMuteIcon;
private final int mNormalIconId = R.drawable.ic_notifications;
private final int mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
private final int mSilentIconId = R.drawable.ic_notifications_off_24dp;
- private final boolean mRingNotificationAliased;
-
-
public NotificationVolumePreferenceController(Context context) {
this(context, KEY_NOTIFICATION_VOLUME);
}
public NotificationVolumePreferenceController(Context context, String key) {
super(context, key);
+
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if (mVibrator != null && !mVibrator.hasVibrator()) {
mVibrator = null;
}
- mRingNotificationAliased = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types);
updateRingerMode();
}
+ /**
+ * Allow for notification slider to be enabled in the scenario where the config switches on
+ * while settings page is already on the screen by always configuring the preference, even if it
+ * is currently inactive.
+ */
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ if (mPreference == null) {
+ setupVolPreference(screen);
+ }
+ mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
+ if (mPreference != null) {
+ mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
+ }
+ updateEffectsSuppressor();
+ updatePreferenceIconAndSliderState();
+ }
+
+ /**
+ * Only display the notification slider when the corresponding device config flag is set
+ */
+ private void onDeviceConfigChange(DeviceConfig.Properties properties) {
+ Set changeSet = properties.getKeyset();
+
+ if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+ boolean newVal = properties.getBoolean(
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
+ if (newVal != mSeparateNotification) {
+ mSeparateNotification = newVal;
+ // manually hiding the preference because being unavailable does not do the job
+ if (mPreference != null) {
+ mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
+ }
+ }
+ }
+ }
+
+
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@Override
public void onResume() {
super.onResume();
mReceiver.register(true);
- updateEffectsSuppressor();
- updatePreferenceIconAndSliderState();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ ActivityThread.currentApplication().getMainExecutor(),
+ this::onDeviceConfigChange);
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
@@ -96,16 +138,17 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere
public void onPause() {
super.onPause();
mReceiver.register(false);
+ DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
}
@Override
public int getAvailabilityStatus() {
+ boolean separateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
- // Show separate notification slider if ring/notification are not aliased by AudioManager --
- // if they are, notification volume is controlled by RingVolumePreferenceController.
return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
- && (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext))
&& !mHelper.isSingleVolume()
+ && (separateNotification || !Utils.isVoiceCapable(mContext))
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/notification/RingVolumePreferenceController.java b/src/com/android/settings/notification/RingVolumePreferenceController.java
index a78689f5a0a..7fdb1e16141 100644
--- a/src/com/android/settings/notification/RingVolumePreferenceController.java
+++ b/src/com/android/settings/notification/RingVolumePreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.notification;
+import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -29,6 +30,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ServiceManager;
import android.os.Vibrator;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.Log;
@@ -36,11 +38,13 @@ import android.util.Log;
import androidx.lifecycle.OnLifecycleEvent;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.Objects;
+import java.util.Set;
/**
* This slider can represent both ring and notification, if the corresponding streams are aliased,
@@ -59,24 +63,21 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
private int mMuteIcon;
- /*
- * Whether ring and notification streams are aliased together by AudioManager.
- * If they are, we'll present one volume control for both.
- * If not, we'll present separate volume controls.
- */
- private final boolean mRingAliasNotif;
-
- private final int mNormalIconId;
+ private int mNormalIconId;
@VisibleForTesting
- final int mVibrateIconId;
+ int mVibrateIconId;
@VisibleForTesting
- final int mSilentIconId;
+ int mSilentIconId;
@VisibleForTesting
- final int mTitleId;
+ int mTitleId;
+
+ private boolean mSeparateNotification;
private INotificationManager mNoMan;
+ private static final boolean CONFIG_DEFAULT_VAL = false;
+
public RingVolumePreferenceController(Context context) {
this(context, KEY_RING_VOLUME);
}
@@ -87,29 +88,56 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
if (mVibrator != null && !mVibrator.hasVibrator()) {
mVibrator = null;
}
-
- mRingAliasNotif = isRingAliasNotification();
- if (mRingAliasNotif) {
- mTitleId = R.string.ring_volume_option_title;
-
- mNormalIconId = R.drawable.ic_notifications;
- mSilentIconId = R.drawable.ic_notifications_off_24dp;
- } else {
- mTitleId = R.string.separate_ring_volume_option_title;
-
- mNormalIconId = R.drawable.ic_ring_volume;
- mSilentIconId = R.drawable.ic_ring_volume_off;
- }
- // todo: set a distinct vibrate icon for ring vs notification
- mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
-
+ mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
+ loadPreferenceIconResources(mSeparateNotification);
updateRingerMode();
}
- @VisibleForTesting
- boolean isRingAliasNotification() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types);
+ private void loadPreferenceIconResources(boolean separateNotification) {
+ if (separateNotification) {
+ mTitleId = R.string.separate_ring_volume_option_title;
+ mNormalIconId = R.drawable.ic_ring_volume;
+ mSilentIconId = R.drawable.ic_ring_volume_off;
+ } else {
+ mTitleId = R.string.ring_volume_option_title;
+ mNormalIconId = R.drawable.ic_notifications;
+ mSilentIconId = R.drawable.ic_notifications_off_24dp;
+ }
+ // todo: set a distinct vibrate icon for ring vs notification
+ mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
+ }
+
+ /**
+ * As the responsibility of this slider changes, so should its title & icon
+ */
+ public void onDeviceConfigChange(DeviceConfig.Properties properties) {
+ Set changeSet = properties.getKeyset();
+ if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+ boolean valueUpdated = readSeparateNotificationVolumeConfig();
+ if (valueUpdated) {
+ updateEffectsSuppressor();
+ selectPreferenceIconState();
+ setPreferenceTitle();
+ }
+ }
+ }
+
+ /**
+ * side effect: updates the cached value of the config, and also the icon
+ * @return has the config changed?
+ */
+ private boolean readSeparateNotificationVolumeConfig() {
+ boolean newVal = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
+
+ boolean valueUpdated = newVal != mSeparateNotification;
+ if (valueUpdated) {
+ mSeparateNotification = newVal;
+ loadPreferenceIconResources(newVal);
+ }
+
+ return valueUpdated;
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -117,8 +145,11 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
public void onResume() {
super.onResume();
mReceiver.register(true);
+ readSeparateNotificationVolumeConfig();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ ActivityThread.currentApplication().getMainExecutor(), this::onDeviceConfigChange);
updateEffectsSuppressor();
- updatePreferenceIcon();
+ selectPreferenceIconState();
setPreferenceTitle();
}
@@ -127,6 +158,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
public void onPause() {
super.onPause();
mReceiver.register(false);
+ DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
}
@Override
@@ -170,7 +202,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
final int ringerMode = mHelper.getRingerModeInternal();
if (mRingerMode == ringerMode) return;
mRingerMode = ringerMode;
- updatePreferenceIcon();
+ selectPreferenceIconState();
}
private void updateEffectsSuppressor() {
@@ -190,7 +222,8 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
return;
}
- if (hintsMatch(hints, mRingAliasNotif)) {
+ if (hintsMatch(hints, DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false))) {
mSuppressor = suppressor;
if (mPreference != null) {
final String text = SuppressorHelper.getSuppressionText(mContext, suppressor);
@@ -200,11 +233,11 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
}
@VisibleForTesting
- boolean hintsMatch(int hints, boolean ringNotificationAliased) {
+ boolean hintsMatch(int hints, boolean notificationSeparated) {
return (hints & NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS) != 0
|| (hints & NotificationListenerService.HINT_HOST_DISABLE_EFFECTS) != 0
|| ((hints & NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS)
- != 0 && ringNotificationAliased);
+ != 0 && !notificationSeparated);
}
@VisibleForTesting
@@ -217,7 +250,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
mVibrator = vibrator;
}
- private void updatePreferenceIcon() {
+ private void selectPreferenceIconState() {
if (mPreference != null) {
if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
mPreference.showIcon(mNormalIconId);
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
index d1701599c34..0414565721e 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
@@ -55,13 +55,17 @@ public abstract class VolumeSeekBarPreferenceController extends
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
- mPreference = screen.findPreference(getPreferenceKey());
- mPreference.setCallback(mVolumePreferenceCallback);
- mPreference.setStream(getAudioStream());
- mPreference.setMuteIcon(getMuteIcon());
+ setupVolPreference(screen);
}
}
+ protected void setupVolPreference(PreferenceScreen screen) {
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setCallback(mVolumePreferenceCallback);
+ mPreference.setStream(getAudioStream());
+ mPreference.setMuteIcon(getMuteIcon());
+ }
+
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (mPreference != null) {
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index e3d0805434b..3a4d3f6529e 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -98,7 +98,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
// TODO: notification_settings
AppPermissionPreference(app)
AppStoragePreference(app)
- // TODO: instant_app_launch_supported_domain_urls
+ InstantAppDomainsPreference(app)
AppDataUsagePreference(app)
AppTimeSpentPreference(app)
AppBatteryPreference(app)
diff --git a/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
new file mode 100644
index 00000000000..3a7d50d24c9
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.framework.common.asUser
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+@Composable
+fun InstantAppDomainsPreference(app: ApplicationInfo) {
+ val context = LocalContext.current
+ if (!app.isInstantApp) return
+
+ val presenter = remember { InstantAppDomainsPresenter(context, app) }
+ var openDialog by rememberSaveable { mutableStateOf(false) }
+
+ Preference(object : PreferenceModel {
+ override val title = stringResource(R.string.app_launch_supported_domain_urls_title)
+ override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
+ override val onClick = { openDialog = true }
+ })
+
+ val domainsState = presenter.domainsFlow.collectAsStateWithLifecycle(initialValue = emptySet())
+ if (openDialog) {
+ Dialog(domainsState) {
+ openDialog = false
+ }
+ }
+}
+
+@Composable
+private fun Dialog(domainsState: State>, onDismissRequest: () -> Unit) {
+ AlertDialog(
+ onDismissRequest = onDismissRequest,
+ confirmButton = {},
+ title = {
+ Text(stringResource(R.string.app_launch_supported_domain_urls_title))
+ },
+ text = {
+ Column {
+ domainsState.value.forEach { domain ->
+ Text(
+ text = domain,
+ modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround),
+ )
+ }
+ }
+ },
+ )
+}
+
+private class InstantAppDomainsPresenter(
+ private val context: Context,
+ private val app: ApplicationInfo,
+) {
+ private val userContext = context.asUser(app.userHandle)
+ private val userPackageManager = userContext.packageManager
+
+ val domainsFlow = flow {
+ emit(Utils.getHandledDomains(userPackageManager, app.packageName))
+ }.flowOn(Dispatchers.IO)
+
+ val summaryFlow = domainsFlow.map { entries ->
+ when (entries.size) {
+ 0 -> context.getString(R.string.domain_urls_summary_none)
+ 1 -> context.getString(R.string.domain_urls_summary_one, entries.first())
+ else -> context.getString(R.string.domain_urls_summary_some, entries.first())
+ }
+ }.flowOn(Dispatchers.IO)
+}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
index 7ab311fe2ab..72477b94585 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
@@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.SystemProperties;
@@ -54,11 +54,6 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest {
@Spy
private Context mSpyContext = RuntimeEnvironment.application;
- @Mock
- private BluetoothManager mBluetoothManager;
- @Mock
- private BluetoothAdapter mBluetoothAdapter;
-
private ListPreference mPreference;
private BluetoothMaxConnectedAudioDevicesPreferenceController mController;
@@ -68,15 +63,19 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- doReturn(mBluetoothManager).when(mSpyContext).getSystemService(BluetoothManager.class);
- doReturn(mBluetoothAdapter).when(mBluetoothManager).getAdapter();
// Get XML values without mock
// Setup test list preference using XML values
mPreference = new ListPreference(mSpyContext);
mPreference.setEntries(R.array.bluetooth_max_connected_audio_devices);
mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values);
- doReturn(TEST_MAX_CONNECTED_AUDIO_DEVICES).when(mBluetoothAdapter)
- .getMaxConnectedAudioDevices();
+ // Retrieve default max connected audio devices to a test controlled value
+ try {
+ Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth");
+ TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth"));
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
// Init the actual controller
mController = new BluetoothMaxConnectedAudioDevicesPreferenceController(mSpyContext);
// Construct preference in the controller via a mocked preference screen object
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index 45e80891faa..2245920d046 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -280,8 +280,7 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture());
- assertThat(captor.getValue().toString())
- .isEqualTo("No usage from last full charge");
+ assertThat(captor.getValue().toString()).isEmpty();
}
@Test
@@ -571,7 +570,8 @@ public class AdvancedPowerUsageDetailTest {
public void startBatteryDetailPage_noBatteryUsage_hasBasicData() {
final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class);
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, PACKAGE_NAME[0]);
+ AdvancedPowerUsageDetail.startBatteryDetailPage(
+ mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER);
verify(mActivity).startActivity(captor.capture());
@@ -589,7 +589,8 @@ public class AdvancedPowerUsageDetailTest {
PackageManager.NameNotFoundException {
doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */);
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, PACKAGE_NAME[0]);
+ AdvancedPowerUsageDetail.startBatteryDetailPage(
+ mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
index 96b9e6219c8..7e7ad10d8c1 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java
@@ -18,6 +18,7 @@ package com.android.settings.notification;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -25,10 +26,17 @@ import android.content.Context;
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Vibrator;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.telephony.TelephonyManager;
-import com.android.internal.R;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import org.junit.Before;
import org.junit.Test;
@@ -37,11 +45,12 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowDeviceConfig.class})
public class NotificationVolumePreferenceControllerTest {
-
@Mock
private AudioHelper mHelper;
@Mock
@@ -52,6 +61,11 @@ public class NotificationVolumePreferenceControllerTest {
private Vibrator mVibrator;
@Mock
private Resources mResources;
+ @Mock
+ private PreferenceManager mPreferenceManager;
+
+ private static final String READ_DEVICE_CONFIG_PERMISSION =
+ "android.permission.READ_DEVICE_CONFIG";
private Context mContext;
private NotificationVolumePreferenceController mController;
@@ -87,7 +101,9 @@ public class NotificationVolumePreferenceControllerTest {
public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() {
when(mResources.getBoolean(
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
- when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(true);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
NotificationVolumePreferenceController controller =
new NotificationVolumePreferenceController(mContext);
@@ -105,7 +121,9 @@ public class NotificationVolumePreferenceControllerTest {
public void isAvailable_voiceCapable_separatedFromRing_shouldReturnTrue() {
when(mResources.getBoolean(
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
- when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(false);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
NotificationVolumePreferenceController controller =
new NotificationVolumePreferenceController(mContext);
@@ -170,4 +188,70 @@ public class NotificationVolumePreferenceControllerTest {
.isTrue();
}
+ @Test
+ public void enableSeparateNotificationConfig_controllerBecomesAvailable() {
+ PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
+ VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class);
+ when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
+ when(screen.getContext()).thenReturn(mContext);
+ when(mResources.getBoolean(
+ com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
+ // block the alternative condition to enable controller
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
+ NotificationVolumePreferenceController controller =
+ new NotificationVolumePreferenceController(mContext);
+ when(screen.findPreference(controller.getPreferenceKey()))
+ .thenReturn(volumeSeekBarPreference);
+
+ // allow the controller to subscribe
+ Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext())
+ .grantPermissions(READ_DEVICE_CONFIG_PERMISSION);
+ controller.onResume();
+ controller.displayPreference(screen);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, Boolean.toString(true),
+ false);
+
+ assertThat(controller.getAvailabilityStatus()
+ == BasePreferenceController.AVAILABLE).isTrue();
+ }
+
+ @Test
+ public void disableSeparateNotificationConfig_controllerBecomesUnavailable() {
+ PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
+ VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class);
+ when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
+ when(screen.getContext()).thenReturn(mContext);
+ when(mResources.getBoolean(
+ com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
+
+ // block the alternative condition to enable controller
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
+ NotificationVolumePreferenceController controller =
+ new NotificationVolumePreferenceController(mContext);
+
+ when(screen.findPreference(controller.getPreferenceKey()))
+ .thenReturn(volumeSeekBarPreference);
+
+ Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext())
+ .grantPermissions(READ_DEVICE_CONFIG_PERMISSION);
+ controller.onResume();
+ controller.displayPreference(screen);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
+ assertThat(controller.getAvailabilityStatus()
+ == BasePreferenceController.UNSUPPORTED_ON_DEVICE).isTrue();
+ }
+
}
diff --git a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java
index 02757d52874..1ad26c71546 100644
--- a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java
@@ -27,10 +27,13 @@ import android.content.Context;
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Vibrator;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.telephony.TelephonyManager;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import org.junit.Before;
import org.junit.Test;
@@ -39,9 +42,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowDeviceConfig.class})
public class RingVolumePreferenceControllerTest {
@Mock
@@ -124,9 +129,10 @@ public class RingVolumePreferenceControllerTest {
// todo: verify that the title change is displayed, by examining the underlying preference
@Test
public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() {
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types))
- .thenReturn(false);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
final RingVolumePreferenceController controller =
new RingVolumePreferenceController(mContext);
@@ -138,8 +144,9 @@ public class RingVolumePreferenceControllerTest {
@Test
public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotification() {
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types)).thenReturn(true);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
final RingVolumePreferenceController control = new RingVolumePreferenceController(mContext);
int expectedTitleId = R.string.ring_volume_option_title;
@@ -150,39 +157,39 @@ public class RingVolumePreferenceControllerTest {
@Test
public void setHintsRing_aliased_Matches() {
assertThat(mController.hintsMatch(
- NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue();
+ NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue();
}
@Test
public void setHintsRingNotification_aliased_Matches() {
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
- true)).isTrue();
+ false)).isTrue();
}
@Test
public void setHintNotification_aliased_Matches() {
assertThat(mController
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
- true)).isTrue();
+ false)).isTrue();
}
@Test
public void setHintsRing_unaliased_Matches() {
assertThat(mController.hintsMatch(
- NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue();
+ NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue();
}
@Test
public void setHintsRingNotification_unaliased_Matches() {
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
- false)).isTrue();
+ true)).isTrue();
}
@Test
public void setHintNotification_unaliased_doesNotMatch() {
assertThat(mController
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
- false)).isFalse();
+ true)).isFalse();
}
@Test
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
new file mode 100644
index 00000000000..9782817f742
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.filterToOne
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isDialog
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.testutils.delay
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class InstantAppDomainsPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private lateinit var mockSession: MockitoSession
+
+ @Spy
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ @Before
+ fun setUp() {
+ mockSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .mockStatic(Utils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ whenever(context.packageManager).thenReturn(packageManager)
+ Mockito.doReturn(context).`when`(context).createContextAsUser(any(), anyInt())
+ mockDomains(emptySet())
+ }
+
+ @After
+ fun tearDown() {
+ mockSession.finishMocking()
+ }
+
+ private fun mockDomains(domains: Set) {
+ whenever(Utils.getHandledDomains(packageManager, PACKAGE_NAME)).thenReturn(domains)
+ }
+
+ @Test
+ fun notInstantApp_notDisplayed() {
+ val app = ApplicationInfo()
+
+ setContent(app)
+
+ composeTestRule.onRoot().assertIsNotDisplayed()
+ }
+
+ @Test
+ fun title_displayed() {
+ setContent()
+
+ composeTestRule
+ .onNodeWithText(context.getString(R.string.app_launch_supported_domain_urls_title))
+ .assertIsDisplayed()
+ .assertIsEnabled()
+ }
+
+ @Test
+ fun noDomain() {
+ mockDomains(emptySet())
+
+ setContent()
+
+ composeTestRule.onNodeWithText(context.getString(R.string.domain_urls_summary_none))
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun oneDomain() {
+ mockDomains(setOf("abc"))
+
+ setContent()
+
+ composeTestRule.onNodeWithText("Open abc").assertIsDisplayed()
+ }
+
+ @Test
+ fun twoDomains() {
+ mockDomains(setOf("abc", "def"))
+
+ setContent()
+
+ composeTestRule.onNodeWithText("Open abc and other URLs").assertIsDisplayed()
+ }
+
+ @Test
+ fun whenClicked() {
+ mockDomains(setOf("abc", "def"))
+
+ setContent()
+ composeTestRule.onRoot().performClick()
+ composeTestRule.delay()
+
+ assertDialogHasText(context.getString(R.string.app_launch_supported_domain_urls_title))
+ assertDialogHasText("abc")
+ assertDialogHasText("def")
+ }
+
+ private fun assertDialogHasText(text: String) {
+ composeTestRule.onAllNodes(hasAnyAncestor(isDialog()))
+ .filterToOne(hasText(text))
+ .assertIsDisplayed()
+ }
+
+ private fun setContent(app:ApplicationInfo = INSTANT_APP) {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ InstantAppDomainsPreference(app)
+ }
+ }
+ }
+
+ private companion object {
+ const val PACKAGE_NAME = "package.name"
+ const val UID = 123
+
+ val INSTANT_APP = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ uid = UID
+ privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
+ }
+ }
+}