diff --git a/res/values/strings.xml b/res/values/strings.xml index ddbd5f22ccb..fa79cbbb33b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7151,6 +7151,18 @@ Notification volume + + Ringer silent + + + Ringer vibrate + + + Notification volume muted, notifications will vibrate + + + %1$s muted + Unavailable because ring is muted diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java index 0767e65bbcb..6bee62cc688 100644 --- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java @@ -81,6 +81,8 @@ public class ApprovalPreferenceController extends BasePreferenceController { final RestrictedSwitchPreference preference = (RestrictedSwitchPreference) pref; final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm); + final boolean isAllowedCn = mCn.flattenToShortString().length() + <= NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH; final boolean isEnabled = isServiceEnabled(mCn); preference.setChecked(isEnabled); preference.setOnPreferenceChangeListener((p, newValue) -> { @@ -105,7 +107,8 @@ public class ApprovalPreferenceController extends BasePreferenceController { return false; } }); - preference.updateState(mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isEnabled); + preference.updateState( + mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isAllowedCn, isEnabled); } public void disable(final ComponentName cn) { diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index be090e33366..fb3319c3ff6 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -623,9 +623,9 @@ public class FingerprintSettings extends SubSettings { return; // Activity went away } - final Preference addPreference = findPreference(KEY_FINGERPRINT_ADD); + mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD); - if (addPreference == null) { + if (mAddFingerprintPreference == null) { return; // b/275519315 Skip if updateAddPreference() invoke before addPreference() } diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index e40a2b4af98..79df55a0048 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -52,6 +52,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont public MediaVolumePreferenceController(Context context) { super(context, KEY_MEDIA_VOLUME); + mVolumePreferenceListener = this::updateContentDescription; } @Override @@ -109,6 +110,18 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont return false; } + private void updateContentDescription() { + if (mPreference != null) { + if (mPreference.isMuted()) { + mPreference.updateContentDescription( + mContext.getString(R.string.volume_content_description_silent_mode, + mPreference.getTitle())); + } else { + mPreference.updateContentDescription(mPreference.getTitle()); + } + } + } + @Override public SliceAction getSliceEndItem(Context context) { if (!isSupportEndItem()) { diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java index dfe6df2a5ca..a6b565ae6ba 100644 --- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java @@ -67,7 +67,9 @@ public class NotificationAccessConfirmationActivity extends Activity mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL); CharSequence mAppLabel; - if (mComponentName == null || mComponentName.getPackageName() == null) { + if (mComponentName == null || mComponentName.getPackageName() == null + || mComponentName.flattenToString().length() + > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { finish(); return; } diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java index 369c4f6dfaf..e2ef0ddccb4 100644 --- a/src/com/android/settings/notification/NotificationAccessSettings.java +++ b/src/com/android/settings/notification/NotificationAccessSettings.java @@ -66,7 +66,6 @@ public class NotificationAccessSettings extends EmptyTextSettings { private static final String TAG = "NotifAccessSettings"; static final String ALLOWED_KEY = "allowed"; static final String NOT_ALLOWED_KEY = "not_allowed"; - private static final int MAX_CN_LENGTH = 500; private static final ManagedServiceSettings.Config CONFIG = new ManagedServiceSettings.Config.Builder() @@ -150,7 +149,8 @@ public class NotificationAccessSettings extends EmptyTextSettings { for (ServiceInfo service : services) { final ComponentName cn = new ComponentName(service.packageName, service.name); boolean isAllowed = mNm.isNotificationListenerAccessGranted(cn); - if (!isAllowed && cn.flattenToString().length() > MAX_CN_LENGTH) { + if (!isAllowed && cn.flattenToString().length() + > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { continue; } diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java index cf8a33f765c..fe7b70bf129 100644 --- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java +++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java @@ -26,6 +26,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.service.notification.NotificationListenerService; +import android.view.View; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.PreferenceScreen; @@ -75,6 +76,7 @@ public class NotificationVolumePreferenceController extends updateEffectsSuppressor(); selectPreferenceIconState(); + updateContentDescription(); updateEnabledState(); } @@ -120,23 +122,37 @@ public class NotificationVolumePreferenceController extends } @Override - protected void selectPreferenceIconState() { + protected int getEffectiveRingerMode() { + if (mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { + return AudioManager.RINGER_MODE_SILENT; + } else if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { + if (mHelper.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0) { + // Ring is in normal, but notification is in silent. + return AudioManager.RINGER_MODE_SILENT; + } + } + return mRingerMode; + } + + @Override + protected void updateContentDescription() { if (mPreference != null) { - if (mVibrator != null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { - mMuteIcon = mVibrateIconId; - mPreference.showIcon(mVibrateIconId); - } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT - || mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { - mMuteIcon = mSilentIconId; - mPreference.showIcon(mSilentIconId); - } else { // ringmode normal: could be that we are still silent - if (mHelper.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0) { - // ring is in normal, but notification is in silent - mMuteIcon = mSilentIconId; - mPreference.showIcon(mSilentIconId); - } else { - mPreference.showIcon(mNormalIconId); - } + int ringerMode = getEffectiveRingerMode(); + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + mPreference.updateContentDescription( + mContext.getString( + R.string.notification_volume_content_description_vibrate_mode)); + } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { + mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + mPreference.updateContentDescription( + mContext.getString(R.string.volume_content_description_silent_mode, + mPreference.getTitle())); + } else { + // Set a11y mode to none in order not to trigger talkback while changing + // notification volume in normal mode. + mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE); + mPreference.updateContentDescription(mPreference.getTitle()); } } } @@ -169,6 +185,7 @@ public class NotificationVolumePreferenceController extends break; case NOTIFICATION_VOLUME_CHANGED: selectPreferenceIconState(); + updateContentDescription(); updateEnabledState(); break; } diff --git a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java index 36877707257..ab65f8f5a9d 100644 --- a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java +++ b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java @@ -26,6 +26,7 @@ import android.os.Vibrator; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; import java.util.Objects; @@ -54,6 +55,7 @@ public abstract class RingerModeAffectedVolumePreferenceController extends if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } + mVolumePreferenceListener = this::updateContentDescription; } protected void updateEffectsSuppressor() { @@ -123,6 +125,7 @@ public abstract class RingerModeAffectedVolumePreferenceController extends } mRingerMode = ringerMode; selectPreferenceIconState(); + updateContentDescription(); return true; } @@ -131,10 +134,11 @@ public abstract class RingerModeAffectedVolumePreferenceController extends */ protected void selectPreferenceIconState() { if (mPreference != null) { - if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { + int ringerMode = getEffectiveRingerMode(); + if (ringerMode == AudioManager.RINGER_MODE_NORMAL) { mPreference.showIcon(mNormalIconId); } else { - if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE && mVibrator != null) { + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { mMuteIcon = mVibrateIconId; } else { mMuteIcon = mSilentIconId; @@ -144,6 +148,28 @@ public abstract class RingerModeAffectedVolumePreferenceController extends } } + protected int getEffectiveRingerMode() { + if (mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { + return AudioManager.RINGER_MODE_SILENT; + } + return mRingerMode; + } + + protected void updateContentDescription() { + if (mPreference != null) { + int ringerMode = getEffectiveRingerMode(); + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + mPreference.updateContentDescription( + mContext.getString(R.string.ringer_content_description_vibrate_mode)); + } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { + mPreference.updateContentDescription( + mContext.getString(R.string.ringer_content_description_silent_mode)); + } else { + mPreference.updateContentDescription(mPreference.getTitle()); + } + } + } + protected abstract boolean hintsMatch(int hints); } diff --git a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java index b8a99085f6d..91926e3c977 100644 --- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java +++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java @@ -65,6 +65,7 @@ public class SeparateRingVolumePreferenceController extends mReceiver.register(true); updateEffectsSuppressor(); selectPreferenceIconState(); + updateContentDescription(); if (mPreference != null) { mPreference.setVisible(getAvailabilityStatus() == AVAILABLE); diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java index 14955c426e1..0000eba2ba7 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreference.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java @@ -47,10 +47,13 @@ public class VolumeSeekBarPreference extends SeekBarPreference { protected SeekBar mSeekBar; private int mStream; - private SeekBarVolumizer mVolumizer; + @VisibleForTesting + SeekBarVolumizer mVolumizer; private Callback mCallback; + private Listener mListener; private ImageView mIconView; private TextView mSuppressionTextView; + private TextView mTitle; private String mSuppressionText; private boolean mMuted; private boolean mZenMuted; @@ -98,6 +101,10 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mCallback = callback; } + public void setListener(Listener listener) { + mListener = listener; + } + public void onActivityResume() { if (mStopped) { init(); @@ -118,6 +125,7 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon); mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text); + mTitle = (TextView) view.findViewById(com.android.internal.R.id.title); init(); } @@ -142,6 +150,9 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mMuted = muted; mZenMuted = zenMuted; updateIconView(); + if (mListener != null) { + mListener.onUpdateMuteState(); + } } @Override public void onStartTrackingTouch(SeekBarVolumizer sbv) { @@ -165,6 +176,9 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mVolumizer.setSeekBar(mSeekBar); updateIconView(); updateSuppressionText(); + if (mListener != null) { + mListener.onUpdateMuteState(); + } if (!isEnabled()) { mSeekBar.setEnabled(false); mVolumizer.stop(); @@ -175,7 +189,7 @@ public class VolumeSeekBarPreference extends SeekBarPreference { if (mIconView == null) return; if (mIconResId != 0) { mIconView.setImageResource(mIconResId); - } else if (mMuteIconResId != 0 && mMuted && !mZenMuted) { + } else if (mMuteIconResId != 0 && isMuted()) { mIconView.setImageResource(mMuteIconResId); } else { mIconView.setImageDrawable(getIcon()); @@ -208,6 +222,10 @@ public class VolumeSeekBarPreference extends SeekBarPreference { updateSuppressionText(); } + protected boolean isMuted() { + return mMuted && !mZenMuted; + } + protected void updateSuppressionText() { if (mSuppressionTextView != null && mSeekBar != null) { mSuppressionTextView.setText(mSuppressionText); @@ -216,6 +234,19 @@ public class VolumeSeekBarPreference extends SeekBarPreference { } } + /** + * Update content description of title to improve talkback announcements. + */ + protected void updateContentDescription(CharSequence contentDescription) { + if (mTitle == null) return; + mTitle.setContentDescription(contentDescription); + } + + protected void setAccessibilityLiveRegion(int mode) { + if (mTitle == null) return; + mTitle.setAccessibilityLiveRegion(mode); + } + public interface Callback { void onSampleStarting(SeekBarVolumizer sbv); void onStreamValueChanged(int stream, int progress); @@ -225,4 +256,15 @@ public class VolumeSeekBarPreference extends SeekBarPreference { */ void onStartTrackingTouch(SeekBarVolumizer sbv); } + + /** + * Listener to view updates in volumeSeekbarPreference. + */ + public interface Listener { + + /** + * Listener to mute state updates. + */ + void onUpdateMuteState(); + } } diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java index 0414565721e..285e8ddbeb9 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java @@ -36,6 +36,7 @@ public abstract class VolumeSeekBarPreferenceController extends protected VolumeSeekBarPreference mPreference; protected VolumeSeekBarPreference.Callback mVolumePreferenceCallback; protected AudioHelper mHelper; + protected VolumeSeekBarPreference.Listener mVolumePreferenceListener; public VolumeSeekBarPreferenceController(Context context, String key) { super(context, key); @@ -62,6 +63,7 @@ public abstract class VolumeSeekBarPreferenceController extends protected void setupVolPreference(PreferenceScreen screen) { mPreference = screen.findPreference(getPreferenceKey()); mPreference.setCallback(mVolumePreferenceCallback); + mPreference.setListener(mVolumePreferenceListener); mPreference.setStream(getAudioStream()); mPreference.setMuteIcon(getMuteIcon()); } diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java new file mode 100644 index 00000000000..86631ffb2d4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.notification; + +import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_COMPONENT_NAME; + +import static com.google.common.truth.Truth.assertThat; + +import static org.robolectric.Shadows.shadowOf; + +import android.annotation.Nullable; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.widget.TextView; + +import com.android.settings.R; + +import com.google.common.base.Strings; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class NotificationAccessConfirmationActivityTest { + + @Test + public void start_showsDialog() { + ComponentName cn = new ComponentName("com.example", "com.example.SomeService"); + installPackage(cn.getPackageName(), "X"); + + NotificationAccessConfirmationActivity activity = startActivityWithIntent(cn); + + assertThat(activity.isFinishing()).isFalse(); + assertThat(getDialogText(activity)).isEqualTo( + activity.getString(R.string.notification_listener_security_warning_summary, "X")); + } + + @Test + public void start_withMissingPackage_finishes() { + ComponentName cn = new ComponentName("com.example", "com.example.SomeService"); + + NotificationAccessConfirmationActivity activity = startActivityWithIntent(cn); + + assertThat(getDialogText(activity)).isNull(); + assertThat(activity.isFinishing()).isTrue(); + } + + @Test + public void start_componentNameTooLong_finishes() { + ComponentName longCn = new ComponentName("com.example", Strings.repeat("Blah", 150)); + installPackage(longCn.getPackageName(), ""); + + NotificationAccessConfirmationActivity activity = startActivityWithIntent(longCn); + + assertThat(getDialogText(activity)).isNull(); + assertThat(activity.isFinishing()).isTrue(); + } + + private static NotificationAccessConfirmationActivity startActivityWithIntent( + ComponentName cn) { + return Robolectric.buildActivity( + NotificationAccessConfirmationActivity.class, + new Intent().putExtra(EXTRA_COMPONENT_NAME, cn)) + .setup() + .get(); + } + + private static void installPackage(String packageName, String appName) { + PackageInfo pi = new PackageInfo(); + pi.packageName = packageName; + pi.applicationInfo = new ApplicationInfo(); + pi.applicationInfo.packageName = packageName; + pi.applicationInfo.name = appName; + shadowOf(RuntimeEnvironment.application.getPackageManager()).installPackage(pi); + } + + @Nullable + private static String getDialogText(Activity activity) { + TextView tv = activity.getWindow().findViewById(android.R.id.message); + CharSequence text = (tv != null ? tv.getText() : null); + return text != null ? text.toString() : null; + } + +} diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java index 2d54c38e249..f7e32a2c9d9 100644 --- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java @@ -49,6 +49,8 @@ public class VolumeSeekBarPreferenceControllerTest { @Mock private VolumeSeekBarPreference.Callback mCallback; @Mock + private VolumeSeekBarPreference.Listener mListener; + @Mock private AudioHelper mHelper; private VolumeSeekBarPreferenceControllerTestable mController; @@ -59,7 +61,7 @@ public class VolumeSeekBarPreferenceControllerTest { when(mScreen.findPreference(nullable(String.class))).thenReturn(mPreference); when(mPreference.getKey()).thenReturn("key"); mController = new VolumeSeekBarPreferenceControllerTestable(mContext, mCallback, true, - mPreference.getKey()); + mPreference.getKey(), mListener); mController.setAudioHelper(mHelper); } @@ -70,18 +72,20 @@ public class VolumeSeekBarPreferenceControllerTest { verify(mPreference).setCallback(mCallback); verify(mPreference).setStream(VolumeSeekBarPreferenceControllerTestable.AUDIO_STREAM); verify(mPreference).setMuteIcon(VolumeSeekBarPreferenceControllerTestable.MUTE_ICON); + verify(mPreference).setListener(mListener); } @Test public void displayPreference_notAvailable_shouldNotUpdatePreference() { mController = new VolumeSeekBarPreferenceControllerTestable(mContext, mCallback, false, - mPreference.getKey()); + mPreference.getKey(), mListener); mController.displayPreference(mScreen); verify(mPreference, never()).setCallback(any(VolumeSeekBarPreference.Callback.class)); verify(mPreference, never()).setStream(anyInt()); verify(mPreference, never()).setMuteIcon(anyInt()); + verify(mPreference, never()).setListener(mListener); } @Test @@ -157,10 +161,12 @@ public class VolumeSeekBarPreferenceControllerTest { private boolean mAvailable; VolumeSeekBarPreferenceControllerTestable(Context context, - VolumeSeekBarPreference.Callback callback, boolean available, String key) { + VolumeSeekBarPreference.Callback callback, boolean available, String key, + VolumeSeekBarPreference.Listener listener) { super(context, key); setCallback(callback); mAvailable = available; + mVolumePreferenceListener = listener; } @Override diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java index d74f76a7cea..59f0bcb91b9 100644 --- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java @@ -18,11 +18,14 @@ package com.android.settings.notification; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.media.AudioManager; +import android.preference.SeekBarVolumizer; +import android.widget.SeekBar; import org.junit.Before; import org.junit.Test; @@ -34,18 +37,28 @@ import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class VolumeSeekBarPreferenceTest { + private static final CharSequence CONTENT_DESCRIPTION = "TEST"; @Mock private AudioManager mAudioManager; @Mock private VolumeSeekBarPreference mPreference; @Mock private Context mContext; + @Mock + private SeekBar mSeekBar; + @Mock + private SeekBarVolumizer mVolumizer; + private VolumeSeekBarPreference.Listener mListener; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager); + doCallRealMethod().when(mPreference).updateContentDescription(CONTENT_DESCRIPTION); + mPreference.mSeekBar = mSeekBar; mPreference.mAudioManager = mAudioManager; + mPreference.mVolumizer = mVolumizer; + mListener = () -> mPreference.updateContentDescription(CONTENT_DESCRIPTION); } @Test @@ -65,4 +78,24 @@ public class VolumeSeekBarPreferenceTest { verify(mPreference).setMin(min); verify(mPreference).setProgress(progress); } + + @Test + public void init_listenerIsCalled() { + doCallRealMethod().when(mPreference).setListener(mListener); + doCallRealMethod().when(mPreference).init(); + + mPreference.setListener(mListener); + mPreference.init(); + + verify(mPreference).updateContentDescription(CONTENT_DESCRIPTION); + } + + @Test + public void init_listenerNotSet_noException() { + doCallRealMethod().when(mPreference).init(); + + mPreference.init(); + + verify(mPreference, never()).updateContentDescription(CONTENT_DESCRIPTION); + } } diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java index 249b713987c..4601a1cfbaa 100644 --- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java @@ -83,6 +83,36 @@ public class ApprovalPreferenceControllerTest { } + @Test + public void updateState_enabled() { + when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( + AppOpsManager.MODE_ALLOWED); + when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true); + RestrictedSwitchPreference pref = new RestrictedSwitchPreference( + mContext); + pref.setAppOps(mAppOpsManager); + + mController.updateState(pref); + + assertThat(pref.isEnabled()).isTrue(); + } + + @Test + public void updateState_invalidCn_disabled() { + ComponentName longCn = new ComponentName("com.example.package", + com.google.common.base.Strings.repeat("Blah", 150)); + mController.setCn(longCn); + when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn( + AppOpsManager.MODE_ALLOWED); + RestrictedSwitchPreference pref = new RestrictedSwitchPreference( + mContext); + pref.setAppOps(mAppOpsManager); + + mController.updateState(pref); + + assertThat(pref.isEnabled()).isFalse(); + } + @Test public void updateState_checked() { when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(