diff options
14 files changed, 517 insertions, 88 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 88d6943b4071..367058fa58dd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -57,7 +57,6 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; -import android.media.AudioManager; import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; @@ -82,6 +81,8 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; +import androidx.lifecycle.Observer; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; @@ -97,6 +98,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.util.Assert; +import com.android.systemui.util.RingerModeTracker; import com.google.android.collect.Lists; @@ -258,6 +260,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private TrustManager mTrustManager; private UserManager mUserManager; private KeyguardBypassController mKeyguardBypassController; + private RingerModeTracker mRingerModeTracker; private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; private int mFaceRunningState = BIOMETRIC_STATE_STOPPED; private LockPatternUtils mLockPatternUtils; @@ -295,6 +298,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Handler mHandler; + private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer ringer) { + mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, ringer, 0).sendToTarget(); + } + }; + private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray(); private BiometricManager mBiometricManager; private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = @@ -1145,9 +1155,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState) .sendToTarget(); - } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, - intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); @@ -1501,6 +1508,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mUserTrustIsUsuallyManaged.delete(userId); } + private void registerRingerTracker() { + mRingerModeTracker.getRingerMode().observeForever(mRingerModeObserver); + } + @VisibleForTesting @Inject protected KeyguardUpdateMonitor( @@ -1508,6 +1519,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, DumpManager dumpManager, + RingerModeTracker ringerModeTracker, @Background Executor backgroundExecutor) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); @@ -1515,6 +1527,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); mBackgroundExecutor = backgroundExecutor; mBroadcastDispatcher = broadcastDispatcher; + mRingerModeTracker = ringerModeTracker; dumpManager.registerDumpable(getClass().getName(), this); mHandler = new Handler(mainLooper) { @@ -1648,10 +1661,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab filter.addAction(Intent.ACTION_SERVICE_STATE); filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler); + mHandler.post(this::registerRingerTracker); + final IntentFilter allUserFilter = new IntentFilter(); allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); @@ -2802,6 +2816,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver); + mRingerModeTracker.getRingerMode().removeObserver(mRingerModeObserver); mHandler.removeCallbacksAndMessages(null); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 5b3d5c565472..82ccb17a52c6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -74,6 +74,8 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerServiceImpl; +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.RingerModeTrackerImpl; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogControllerImpl; @@ -264,4 +266,10 @@ public abstract class DependencyBinder { @Binds public abstract VolumeComponent provideVolumeComponent( VolumeDialogComponent volumeDialogComponent); + + /** + */ + @Binds + public abstract RingerModeTracker provideRingerModeTracker( + RingerModeTrackerImpl ringerModeTrackerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index f5293ccf0b0f..5aed64793711 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -78,6 +78,11 @@ import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; @@ -114,6 +119,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; +import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; @@ -130,7 +136,8 @@ import javax.inject.Inject; public class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener, - GlobalActionsPanelPlugin.Callbacks { + GlobalActionsPanelPlugin.Callbacks, + LifecycleOwner { public static final String SYSTEM_DIALOG_REASON_KEY = "reason"; public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; @@ -179,6 +186,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final NotificationShadeDepthController mDepthController; private final BlurUtils mBlurUtils; + // Used for RingerModeTracker + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + private ArrayList<Action> mItems; private ActionsDialog mDialog; @@ -209,6 +219,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>(); private ControlsController mControlsController; private SharedPreferences mControlsPreferences; + private final RingerModeTracker mRingerModeTracker; @VisibleForTesting public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { @@ -247,7 +258,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ControlsUiController controlsUiController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, ControlsListingController controlsListingController, - ControlsController controlsController, UiEventLogger uiEventLogger) { + ControlsController controlsController, UiEventLogger uiEventLogger, + RingerModeTracker ringerModeTracker) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -274,6 +286,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBackgroundExecutor = backgroundExecutor; mControlsListingController = controlsListingController; mBlurUtils = blurUtils; + mRingerModeTracker = ringerModeTracker; mControlsController = controlsController; // receive broadcasts @@ -294,6 +307,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean( R.bool.config_useFixedVolume); + if (mShowSilentToggle) { + mRingerModeTracker.getRingerMode().observe(this, ringer -> + mHandler.sendEmptyMessage(MESSAGE_REFRESH) + ); + } mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); mScreenshotHelper = new ScreenshotHelper(context); @@ -1001,16 +1019,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, refreshSilentMode(); mAirplaneModeOn.updateState(mAirplaneState); mAdapter.notifyDataSetChanged(); - if (mShowSilentToggle) { - IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); - mBroadcastDispatcher.registerReceiver(mRingerModeReceiver, filter); - } + mLifecycle.setCurrentState(Lifecycle.State.RESUMED); } private void refreshSilentMode() { if (!mHasVibrator) { - final boolean silentModeOn = - mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; + Integer value = mRingerModeTracker.getRingerMode().getValue(); + final boolean silentModeOn = value != null && value != AudioManager.RINGER_MODE_NORMAL; ((ToggleAction) mSilentModeAction).updateState( silentModeOn ? ToggleState.On : ToggleState.Off); } @@ -1024,14 +1039,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mDialog = null; } mWindowManagerFuncs.onGlobalActionsHidden(); - if (mShowSilentToggle) { - try { - mBroadcastDispatcher.unregisterReceiver(mRingerModeReceiver); - } catch (IllegalArgumentException ie) { - // ignore this - Log.w(TAG, ie); - } - } + mLifecycle.setCurrentState(Lifecycle.State.DESTROYED); } /** @@ -1578,15 +1586,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }; - private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { - mHandler.sendEmptyMessage(MESSAGE_REFRESH); - } - } - }; - private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -1652,6 +1651,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } + private static final class ActionsDialog extends Dialog implements DialogInterface, ColorExtractor.OnColorsChangedListener { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index e1ffad4df2c3..90dc38f3ad12 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -22,10 +22,8 @@ import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.ColorInt; import android.app.ActivityManager; import android.app.AlarmManager; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; @@ -49,13 +47,16 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.DualToneHandler; import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; @@ -70,6 +71,7 @@ import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeTracker; import java.util.ArrayList; import java.util.List; @@ -86,7 +88,7 @@ import javax.inject.Named; */ public class QuickStatusBarHeader extends RelativeLayout implements View.OnClickListener, NextAlarmController.NextAlarmChangeCallback, - ZenModeController.Callback { + ZenModeController.Callback, LifecycleOwner { private static final String TAG = "QuickStatusBarHeader"; private static final boolean DEBUG = false; @@ -137,15 +139,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements private DateView mDateView; private BatteryMeterView mBatteryRemainingIcon; - private BroadcastDispatcher mBroadcastDispatcher; + // Used for RingerModeTracker + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); - private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - updateStatusText(); - } - }; private boolean mHasTopCutout = false; private int mRoundedCornerPadding = 0; @@ -154,7 +150,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements NextAlarmController nextAlarmController, ZenModeController zenModeController, StatusBarIconController statusBarIconController, ActivityStarter activityStarter, - CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher) { + CommandQueue commandQueue, RingerModeTracker ringerModeTracker) { super(context, attrs); mAlarmController = nextAlarmController; mZenController = zenModeController; @@ -162,8 +158,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements mActivityStarter = activityStarter; mDualToneHandler = new DualToneHandler( new ContextThemeWrapper(context, R.style.QSHeaderTheme)); - mBroadcastDispatcher = broadcastDispatcher; mCommandQueue = commandQueue; + ringerModeTracker.getRingerModeInternal().observe(this, ringer -> { + mRingerMode = ringer; + updateStatusText(); + }); } @Override @@ -484,12 +483,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (listening) { mZenController.addCallback(this); mAlarmController.addCallback(this); - mBroadcastDispatcher.registerReceiver(mRingerReceiver, - new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); + mLifecycle.setCurrentState(Lifecycle.State.RESUMED); } else { mZenController.removeCallback(this); mAlarmController.removeCallback(this); - mBroadcastDispatcher.unregisterReceiver(mRingerReceiver); + mLifecycle.setCurrentState(Lifecycle.State.DESTROYED); } } @@ -586,4 +584,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements lp.rightMargin = sideMargins; } } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 14af466a2424..d4f4d3bfbb54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -39,6 +39,8 @@ import android.telecom.TelecomManager; import android.text.format.DateFormat; import android.util.Log; +import androidx.lifecycle.Observer; + import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -64,6 +66,7 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.time.DateFormatUtil; import java.util.Locale; @@ -109,7 +112,6 @@ public class PhoneStatusBarPolicy private final SharedPreferences mSharedPreferences; private final DateFormatUtil mDateFormatUtil; private final TelecomManager mTelecomManager; - private final AudioManager mAudioManager; private final Handler mHandler = new Handler(); private final CastController mCast; @@ -132,6 +134,7 @@ public class PhoneStatusBarPolicy private final Executor mUiBgExecutor; private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; + private final RingerModeTracker mRingerModeTracker; private boolean mZenVisible; private boolean mVolumeVisible; @@ -154,10 +157,11 @@ public class PhoneStatusBarPolicy KeyguardStateController keyguardStateController, LocationController locationController, SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, - AlarmManager alarmManager, UserManager userManager, AudioManager audioManager, + AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, - @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) { + @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, + RingerModeTracker ringerModeTracker) { mIconController = iconController; mCommandQueue = commandQueue; mBroadcastDispatcher = broadcastDispatcher; @@ -179,8 +183,8 @@ public class PhoneStatusBarPolicy mSensorPrivacyController = sensorPrivacyController; mRecordingController = recordingController; mUiBgExecutor = uiBgExecutor; - mAudioManager = audioManager; mTelecomManager = telecomManager; + mRingerModeTracker = ringerModeTracker; mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); @@ -208,8 +212,7 @@ public class PhoneStatusBarPolicy public void init() { // listen for broadcasts IntentFilter filter = new IntentFilter(); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + filter.addAction(AudioManager.ACTION_HEADSET_PLUG); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); @@ -217,6 +220,10 @@ public class PhoneStatusBarPolicy filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); + Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen); + + mRingerModeTracker.getRingerMode().observeForever(observer); + mRingerModeTracker.getRingerModeInternal().observeForever(observer); // listen for user / profile change. try { @@ -350,14 +357,18 @@ public class PhoneStatusBarPolicy } if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) { - if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_vibrate; - volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); - } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_silent; - volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); + final Integer ringerModeInternal = + mRingerModeTracker.getRingerModeInternal().getValue(); + if (ringerModeInternal != null) { + if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { + volumeVisible = true; + volumeIconId = R.drawable.stat_sys_ringer_vibrate; + volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); + } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { + volumeVisible = true; + volumeIconId = R.drawable.stat_sys_ringer_silent; + volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); + } } } @@ -616,10 +627,6 @@ public class PhoneStatusBarPolicy public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { - case AudioManager.RINGER_MODE_CHANGED_ACTION: - case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION: - updateVolumeZen(); - break; case Intent.ACTION_SIM_STATE_CHANGED: // Avoid rebroadcast because SysUI is direct boot aware. if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt new file mode 100644 index 000000000000..047298e6ed81 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import androidx.lifecycle.LiveData + +interface RingerModeTracker { + + val ringerMode: LiveData<Int> + val ringerModeInternal: LiveData<Int> +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt new file mode 100644 index 000000000000..58684c386995 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.media.AudioManager +import android.os.UserHandle +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.qualifiers.Background +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RingerModeTrackerImpl @Inject constructor( + audioManager: AudioManager, + broadcastDispatcher: BroadcastDispatcher, + @Background executor: Executor +) : RingerModeTracker { + + override val ringerMode: LiveData<Int> = RingerModeLiveData( + broadcastDispatcher, + executor, + AudioManager.RINGER_MODE_CHANGED_ACTION, + audioManager::getRingerMode + ) + + override val ringerModeInternal: LiveData<Int> = RingerModeLiveData( + broadcastDispatcher, + executor, + AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, + audioManager::getRingerModeInternal + ) +} + +class RingerModeLiveData( + private val broadcastDispatcher: BroadcastDispatcher, + private val executor: Executor, + intent: String, + private val getter: () -> Int +) : MutableLiveData<Int>() { + + private val filter = IntentFilter(intent) + var initialSticky: Boolean = false + private set + + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + initialSticky = isInitialStickyBroadcast + postValue(intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1)) + } + } + + override fun getValue(): Int { + return super.getValue() ?: -1 + } + + override fun onActive() { + super.onActive() + broadcastDispatcher.registerReceiver(receiver, filter, executor, UserHandle.ALL) + executor.execute { + postValue(getter()) + } + } + + override fun onInactive() { + super.onInactive() + broadcastDispatcher.unregisterReceiver(receiver) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 73532632c875..f19c49cc123f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -55,6 +55,8 @@ import android.util.Log; import android.util.Slog; import android.view.accessibility.AccessibilityManager; +import androidx.lifecycle.Observer; + import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; @@ -64,6 +66,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -122,6 +126,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final NotificationManager mNoMan; private final SettingObserver mObserver; private final Receiver mReceiver = new Receiver(); + private final RingerModeObservers mRingerModeObservers; private final MediaSessions mMediaSessions; protected C mCallbacks = new C(); private final State mState = new State(); @@ -145,7 +150,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa @Inject public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher, - Optional<Lazy<StatusBar>> statusBarOptionalLazy) { + Optional<Lazy<StatusBar>> statusBarOptionalLazy, RingerModeTracker ringerModeTracker) { mContext = context.getApplicationContext(); // TODO(b/150663459): remove this TV workaround once StatusBar is "unbound" on TVs if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { @@ -164,6 +169,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mObserver = new SettingObserver(mWorker); + mRingerModeObservers = new RingerModeObservers( + (RingerModeLiveData) ringerModeTracker.getRingerMode(), + (RingerModeLiveData) ringerModeTracker.getRingerModeInternal() + ); + mRingerModeObservers.init(); mBroadcastDispatcher = broadcastDispatcher; mObserver.init(); mReceiver.init(); @@ -246,6 +256,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mMediaSessions.destroy(); mObserver.destroy(); mReceiver.destroy(); + mRingerModeObservers.destroy(); mWorkerThread.quitSafely(); } @@ -528,7 +539,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa ss.name = STREAMS.get(stream); checkRoutedToBluetoothW(stream); } - updateRingerModeExternalW(mAudio.getRingerMode()); + // We are not destroyed so this is listening and has updated information + updateRingerModeExternalW(mRingerModeObservers.mRingerMode.getValue()); updateZenModeW(); updateZenConfig(); updateEffectsSuppressorW(mNoMan.getEffectsSuppressor()); @@ -575,7 +587,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted); } if (muted && isRinger(stream)) { - updateRingerModeInternalW(mAudio.getRingerModeInternal()); + updateRingerModeInternalW(mRingerModeObservers.mRingerModeInternal.getValue()); } return true; } @@ -964,6 +976,79 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } } + private final class RingerModeObservers { + + private final RingerModeLiveData mRingerMode; + private final RingerModeLiveData mRingerModeInternal; + + private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer value) { + mWorker.post(() -> { + final int rm = value; + if (mRingerMode.getInitialSticky()) { + mState.ringerModeExternal = rm; + } + if (D.BUG) { + Log.d(TAG, "onChange ringer_mode rm=" + + Util.ringerModeToString(rm)); + } + if (updateRingerModeExternalW(rm)) { + mCallbacks.onStateChanged(mState); + } + } + ); + } + }; + + private final Observer<Integer> mRingerModeInternalObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer value) { + mWorker.post(() -> { + final int rm = value; + if (mRingerModeInternal.getInitialSticky()) { + mState.ringerModeInternal = rm; + } + if (D.BUG) { + Log.d(TAG, "onChange internal_ringer_mode rm=" + + Util.ringerModeToString(rm)); + } + if (updateRingerModeInternalW(rm)) { + mCallbacks.onStateChanged(mState); + } + } + ); + } + }; + + RingerModeObservers(RingerModeLiveData ringerMode, + RingerModeLiveData ringerModeInternal) { + mRingerMode = ringerMode; + mRingerModeInternal = ringerModeInternal; + } + + public void init() { + int initialValue = mRingerMode.getValue(); + if (initialValue != -1) { + // If it's not -1, set it to the initial value, if it's -1, it means that the + // tracker is not listening already and will obtain the sticky value. + mState.ringerModeExternal = initialValue; + } + mRingerMode.observeForever(mRingerModeObserver); + initialValue = mRingerModeInternal.getValue(); + if (initialValue != -1) { + // If it's not -1, set it to the initial value, if it's -1, it means that the + // tracker is not listening already and will obtain the sticky value. + mState.ringerModeInternal = initialValue; + } + mRingerModeInternal.observeForever(mRingerModeInternalObserver); + } + + public void destroy() { + mRingerMode.removeObserver(mRingerModeObserver); + mRingerModeInternal.removeObserver(mRingerModeInternalObserver); + } + } private final class SettingObserver extends ContentObserver { private final Uri ZEN_MODE_URI = @@ -1006,8 +1091,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION); filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); @@ -1042,18 +1125,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa + stream + " devices=" + devices + " oldDevices=" + oldDevices); changed = checkRoutedToBluetoothW(stream); changed |= onVolumeChangedW(stream, 0); - } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { - final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm; - if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm=" - + Util.ringerModeToString(rm)); - changed = updateRingerModeExternalW(rm); - } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { - final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm; - if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm=" - + Util.ringerModeToString(rm)); - changed = updateRingerModeInternalW(rm); } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) { final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); final boolean muted = intent diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index d47fceea9724..64590fd3a36b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -25,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,6 +37,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { @UiThreadTest @Test public void showSecurityScreen_canInflateAllModes() { + mDependency.injectMockDependency(KeyguardStateController.class); KeyguardSecurityContainer keyguardSecurityContainer = new KeyguardSecurityContainer(getContext()); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 73f9d8ad6953..7403a11fecbf 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -29,6 +29,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -50,6 +52,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; @@ -63,18 +66,24 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Observer; + import com.android.internal.telephony.TelephonyIntents; import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.util.RingerModeTracker; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -125,6 +134,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private BroadcastDispatcher mBroadcastDispatcher; @Mock private Executor mBackgroundExecutor; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private LiveData<Integer> mRingerModeLiveData; private TestableLooper mTestableLooper; private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -156,6 +169,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager); context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + mTestableLooper = TestableLooper.get(this); allowTestableLooperAsMainThread(); mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context); @@ -613,6 +628,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull(); } + @Test + public void testRingerModeChange() { + ArgumentCaptor<Observer<Integer>> captor = ArgumentCaptor.forClass(Observer.class); + verify(mRingerModeLiveData).observeForever(captor.capture()); + Observer<Integer> observer = captor.getValue(); + + KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); + + mKeyguardUpdateMonitor.registerCallback(callback); + + observer.onChanged(AudioManager.RINGER_MODE_NORMAL); + observer.onChanged(AudioManager.RINGER_MODE_SILENT); + observer.onChanged(AudioManager.RINGER_MODE_VIBRATE); + + mTestableLooper.processAllMessages(); + + InOrder orderVerify = inOrder(callback); + orderVerify.verify(callback).onRingerModeChanged(anyInt()); // Initial update on register + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_NORMAL); + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_SILENT); + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_VIBRATE); + } + private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) { BroadcastReceiver.PendingResult pendingResult = new BroadcastReceiver.PendingResult(Activity.RESULT_OK, @@ -643,7 +681,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { protected TestableKeyguardUpdateMonitor(Context context) { super(context, TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(), - mBroadcastDispatcher, mDumpManager, mBackgroundExecutor); + mBroadcastDispatcher, mDumpManager, + mRingerModeTracker, mBackgroundExecutor); mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 32604f8609fa..b2c35867e789 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -38,6 +38,8 @@ import com.android.systemui.broadcast.FakeBroadcastDispatcher; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.SmartReplyController; +import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import org.junit.After; import org.junit.Before; @@ -97,6 +99,12 @@ public abstract class SysuiTestCase { // A lot of tests get the LocalBluetoothManager, often via several layers of indirection. // None of them actually need it. mDependency.injectMockDependency(LocalBluetoothManager.class); + + // Notifications tests are injecting one of these, causing many classes (including + // KeyguardUpdateMonitor to be created (injected). + // TODO(b/1531701009) Clean up NotificationContentView creation to prevent this + mDependency.injectMockDependency(SmartReplyController.class); + mDependency.injectMockDependency(NotificationBlockingHelperManager.class); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 137a126f539d..300cb212ffb1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -18,6 +18,7 @@ package com.android.systemui.globalactions; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; @@ -52,6 +53,8 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; import org.junit.Before; import org.junit.Test; @@ -95,6 +98,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private ControlsListingController mControlsListingController; @Mock private ControlsController mControlsController; @Mock private UiEventLogger mUiEventLogger; + @Mock private RingerModeTracker mRingerModeTracker; + @Mock private RingerModeLiveData mRingerModeLiveData; private TestableLooper mTestableLooper; @@ -103,6 +108,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); allowTestableLooperAsMainThread(); + + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); mGlobalActionsDialog = new GlobalActionsDialog(mContext, mWindowManagerFuncs, mAudioManager, @@ -133,7 +140,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mBackgroundExecutor, mControlsListingController, mControlsController, - mUiEventLogger + mUiEventLogger, + mRingerModeTracker ); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt new file mode 100644 index 000000000000..2489c301f6f5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import android.content.BroadcastReceiver +import android.content.IntentFilter +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.lifecycle.Observer +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class RingerModeLiveDataTest : SysuiTestCase() { + + companion object { + private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + private fun <T> any(): T = Mockito.any() + private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + private val INTENT = "INTENT" + } + + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var valueSupplier: () -> Int + @Mock + private lateinit var observer: Observer<Int> + @Captor + private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> + @Captor + private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter> + + // Run everything immediately + private val executor = Executor { it.run() } + private lateinit var liveData: RingerModeLiveData + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + liveData = RingerModeLiveData(broadcastDispatcher, executor, INTENT, valueSupplier) + } + + @After + fun tearDown() { + liveData.removeObserver(observer) + } + + @Test + fun testInit_broadcastNotRegistered() { + verifyNoMoreInteractions(broadcastDispatcher) + } + + @Test + fun testOnActive_broadcastRegistered() { + liveData.observeForever(observer) + verify(broadcastDispatcher).registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL)) + } + + @Test + fun testOnActive_intentFilterHasIntent() { + liveData.observeForever(observer) + verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(), + any()) + assertTrue(intentFilterCaptor.value.hasAction(INTENT)) + } + + @Test + fun testOnActive_valueObtained() { + liveData.observeForever(observer) + verify(valueSupplier).invoke() + } + + @Test + fun testOnInactive_broadcastUnregistered() { + liveData.observeForever(observer) + liveData.removeObserver(observer) + verify(broadcastDispatcher).unregisterReceiver(any()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 8cc83dd99b53..6166cd76e005 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -31,6 +31,7 @@ import android.media.session.MediaSession; import android.os.Handler; import android.os.Process; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import androidx.test.filters.SmallTest; @@ -38,7 +39,10 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +53,7 @@ import java.util.Optional; @RunWith(AndroidTestingRunner.class) @SmallTest +@TestableLooper.RunWithLooper public class VolumeDialogControllerImplTest extends SysuiTestCase { TestableVolumeDialogControllerImpl mVolumeController; @@ -56,18 +61,35 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { StatusBar mStatusBar; @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private RingerModeLiveData mRingerModeLiveData; + @Mock + private RingerModeLiveData mRingerModeInternalLiveData; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + when(mRingerModeTracker.getRingerModeInternal()).thenReturn(mRingerModeInternalLiveData); + // Initial non-set value + when(mRingerModeLiveData.getValue()).thenReturn(-1); + when(mRingerModeInternalLiveData.getValue()).thenReturn(-1); + mCallback = mock(VolumeDialogControllerImpl.C.class); mStatusBar = mock(StatusBar.class); mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar, - mBroadcastDispatcher); + mBroadcastDispatcher, mRingerModeTracker); mVolumeController.setEnableDialogs(true, true); } + @After + public void tearDown() { + mVolumeController.destroy(); + } + @Test public void testRegisteredWithDispatcher() { verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class), @@ -109,7 +131,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { TestableVolumeDialogControllerImpl nullStatusBarTestableDialog = new TestableVolumeDialogControllerImpl( - mContext, callback, null, mBroadcastDispatcher); + mContext, callback, null, mBroadcastDispatcher, mRingerModeTracker); nullStatusBarTestableDialog.setEnableDialogs(true, true); nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); @@ -127,12 +149,26 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token); } + @Test + public void testRingerModeLiveDataObserving() { + verify(mRingerModeLiveData).observeForever(any()); + verify(mRingerModeInternalLiveData).observeForever(any()); + } + + @Test + public void testRingerModeOnDestroy_observersRemoved() { + mVolumeController.destroy(); + + verify(mRingerModeLiveData).removeObserver(any()); + verify(mRingerModeInternalLiveData).removeObserver(any()); + } + static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s, - BroadcastDispatcher broadcastDispatcher) { + BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker) { super( context, broadcastDispatcher, - s == null ? Optional.empty() : Optional.of(() -> s)); + s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker); mCallbacks = callback; } } |