diff options
5 files changed, 183 insertions, 42 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index cf7d2c57923c..3d9645a3d983 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -58,9 +58,26 @@ public interface VolumeDialogController { void userActivity(); void getState(); - boolean areCaptionsEnabled(); - void setCaptionsEnabled(boolean isEnabled); - + /** + * Get Captions enabled state + * + * @param checkForSwitchState set true when we'd like to switch captions enabled state after + * getting the latest captions state. + */ + void getCaptionsEnabledState(boolean checkForSwitchState); + + /** + * Set Captions enabled state + * + * @param enabled the captions enabled state we'd like to update. + */ + void setCaptionsEnabledState(boolean enabled); + + /** + * Get Captions component state + * + * @param fromTooltip if it's triggered from tooltip. + */ void getCaptionsComponentState(boolean fromTooltip); @ProvidesInterface(version = StreamState.VERSION) @@ -192,7 +209,22 @@ public interface VolumeDialogController { void onScreenOff(); void onShowSafetyWarning(int flags); void onAccessibilityModeChanged(Boolean showA11yStream); + + /** + * Callback function for captions component state changed event + * + * @param isComponentEnabled the lateset captions component state. + * @param fromTooltip if it's triggered from tooltip. + */ void onCaptionComponentStateChanged(Boolean isComponentEnabled, Boolean fromTooltip); + + /** + * Callback function for captions enabled state changed event + * + * @param isEnabled the lateset captions enabled state. + * @param checkBeforeSwitch intend to switch captions enabled state after the callback. + */ + void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch); // requires version 2 void onShowCsdWarning(@AudioManager.CsdWarning int csdWarning, int durationMs); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index d39a53d311c6..9cc3cdbf5c34 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -44,6 +44,7 @@ import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession.Token; import android.net.Uri; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -57,6 +58,7 @@ import android.util.Slog; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; +import androidx.annotation.NonNull; import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; @@ -81,6 +83,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; @@ -131,7 +134,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final Receiver mReceiver = new Receiver(); private final RingerModeObservers mRingerModeObservers; private final MediaSessions mMediaSessions; - private final CaptioningManager mCaptioningManager; + private final AtomicReference<CaptioningManager> mCaptioningManager = new AtomicReference<>(); private final KeyguardManager mKeyguardManager; private final ActivityManager mActivityManager; private final UserTracker mUserTracker; @@ -155,16 +158,16 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver = new WakefulnessLifecycle.Observer() { - @Override - public void onStartedWakingUp() { - mDeviceInteractive = true; - } + @Override + public void onStartedWakingUp() { + mDeviceInteractive = true; + } - @Override - public void onFinishedGoingToSleep() { - mDeviceInteractive = false; - } - }; + @Override + public void onFinishedGoingToSleep() { + mDeviceInteractive = false; + } + }; @Inject public VolumeDialogControllerImpl( @@ -179,7 +182,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, - CaptioningManager captioningManager, KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, @@ -209,17 +211,19 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mVibrator = vibrator; mHasVibrator = mVibrator.hasVibrator(); mAudioService = iAudioService; - mCaptioningManager = captioningManager; mKeyguardManager = keyguardManager; mActivityManager = activityManager; mUserTracker = userTracker; + mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mWorker)); + createCaptioningManagerServiceByUserContext(mUserTracker.getUserContext()); + dumpManager.registerDumpable("VolumeDialogControllerImpl", this); boolean accessibilityVolumeStreamActive = accessibilityManager .isAccessibilityVolumeStreamActive(); mVolumeController.setA11yMode(accessibilityVolumeStreamActive ? - VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : - VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); + VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : + VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver); } @@ -316,12 +320,31 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mWorker.sendEmptyMessage(W.GET_STATE); } - public boolean areCaptionsEnabled() { - return mCaptioningManager.isSystemAudioCaptioningEnabled(); + /** + * We met issues about the wrong state of System Caption in multi-user mode. + * It happened in the usage of CaptioningManager Service from SysUI process + * that is a global system process of User 0. + * Therefore, we have to add callback on UserTracker that allows us to get the Context of + * active User and then get the corresponding CaptioningManager Service for further usages. + */ + private final UserTracker.Callback mUserChangedCallback = + new UserTracker.Callback() { + @Override + public void onUserChanged(int newUser, @NonNull Context userContext) { + createCaptioningManagerServiceByUserContext(userContext); + } + }; + + private void createCaptioningManagerServiceByUserContext(@NonNull Context userContext) { + mCaptioningManager.set(userContext.getSystemService(CaptioningManager.class)); + } + + public void getCaptionsEnabledState(boolean checkForSwitchState) { + mWorker.obtainMessage(W.GET_CAPTIONS_ENABLED_STATE, checkForSwitchState).sendToTarget(); } - public void setCaptionsEnabled(boolean isEnabled) { - mCaptioningManager.setSystemAudioCaptioningEnabled(isEnabled); + public void setCaptionsEnabledState(boolean enabled) { + mWorker.obtainMessage(W.SET_CAPTIONS_ENABLED_STATE, enabled).sendToTarget(); } public void getCaptionsComponentState(boolean fromTooltip) { @@ -362,8 +385,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) { - mShowVolumeDialog = volumeUi; - mShowSafetyWarning = safetyWarning; + mShowVolumeDialog = volumeUi; + mShowSafetyWarning = safetyWarning; } @Override @@ -414,12 +437,38 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private void onShowCsdWarningW(@AudioManager.CsdWarning int csdWarning, int durationMs) { - mCallbacks.onShowCsdWarning(csdWarning, durationMs); + mCallbacks.onShowCsdWarning(csdWarning, durationMs); } private void onGetCaptionsComponentStateW(boolean fromTooltip) { - mCallbacks.onCaptionComponentStateChanged( - mCaptioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip); + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + mCallbacks.onCaptionComponentStateChanged( + captioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip); + } else { + Log.e(TAG, "onGetCaptionsComponentStateW(), null captioningManager"); + } + } + + private void onGetCaptionsEnabledStateW(boolean checkForSwitchState) { + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + mCallbacks.onCaptionEnabledStateChanged( + captioningManager.isSystemAudioCaptioningEnabled(), checkForSwitchState); + } else { + Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager"); + } + } + + private void onSetCaptionsEnabledStateW(boolean enabled) { + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + captioningManager.setSystemAudioCaptioningEnabled(enabled); + mCallbacks.onCaptionEnabledStateChanged( + captioningManager.isSystemAudioCaptioningEnabled(), false); + } else { + Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager"); + } } private void onAccessibilityModeChanged(Boolean showA11yStream) { @@ -719,7 +768,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa * This method will never be called if the CSD (Computed Sound Dose) feature is * not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of * the feature. - * @param warning the type of warning to display, values are one of + * @param csdWarning the type of warning to display, values are one of * {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X}, * {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X}, * {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE}, @@ -798,6 +847,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final int ACCESSIBILITY_MODE_CHANGED = 15; private static final int GET_CAPTIONS_COMPONENT_STATE = 16; private static final int SHOW_CSD_WARNING = 17; + private static final int GET_CAPTIONS_ENABLED_STATE = 18; + private static final int SET_CAPTIONS_ENABLED_STATE = 19; W(Looper looper) { super(looper); @@ -825,6 +876,10 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj); break; case SHOW_CSD_WARNING: onShowCsdWarningW(msg.arg1, msg.arg2); break; + case GET_CAPTIONS_ENABLED_STATE: + onGetCaptionsEnabledStateW((Boolean) msg.obj); break; + case SET_CAPTIONS_ENABLED_STATE: + onSetCaptionsEnabledStateW((Boolean) msg.obj); break; } } } @@ -993,6 +1048,17 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa componentEnabled, fromTooltip)); } } + + @Override + public void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch) { + boolean captionsEnabled = isEnabled != null && isEnabled; + for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) { + entry.getValue().post( + () -> entry.getKey().onCaptionEnabledStateChanged( + captionsEnabled, checkBeforeSwitch)); + } + } + } private final class RingerModeObservers { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 6219e4d09b43..aafa16f4a5ed 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -1333,21 +1333,30 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (!isServiceComponentEnabled) return; - updateCaptionsIcon(); + checkEnabledStateForCaptionsIconUpdate(); if (fromTooltip) showCaptionsTooltip(); } - private void updateCaptionsIcon() { - boolean captionsEnabled = mController.areCaptionsEnabled(); - if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) { - mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled)); + private void updateCaptionsEnabledH(boolean isCaptionsEnabled, boolean checkForSwitchState) { + if (checkForSwitchState) { + mController.setCaptionsEnabledState(!isCaptionsEnabled); + } else { + updateCaptionsIcon(isCaptionsEnabled); + } + } + + private void checkEnabledStateForCaptionsIconUpdate() { + mController.getCaptionsEnabledState(false); + } + + private void updateCaptionsIcon(boolean isCaptionsEnabled) { + if (mODICaptionsIcon.getCaptionsEnabled() != isCaptionsEnabled) { + mHandler.post(mODICaptionsIcon.setCaptionsEnabled(isCaptionsEnabled)); } } private void onCaptionIconClicked() { - boolean isEnabled = mController.areCaptionsEnabled(); - mController.setCaptionsEnabled(!isEnabled); - updateCaptionsIcon(); + mController.getCaptionsEnabledState(true); } private void incrementManualToggleCount() { @@ -2363,7 +2372,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } else { updateRowsH(activeRow); } - } @Override @@ -2371,6 +2379,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, Boolean isComponentEnabled, Boolean fromTooltip) { updateODICaptionsH(isComponentEnabled, fromTooltip); } + + @Override + public void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkForSwitchState) { + updateCaptionsEnabledH(isEnabled, checkForSwitchState); + } }; @VisibleForTesting void onPostureChanged(int posture) { 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 066300445a94..69d7586e6ab4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -40,7 +40,6 @@ import android.os.Process; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.CaptioningManager; import androidx.test.filters.SmallTest; @@ -64,6 +63,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.Executor; + @RunWith(AndroidTestingRunner.class) @SmallTest @TestableLooper.RunWithLooper @@ -96,8 +97,6 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { @Mock private WakefulnessLifecycle mWakefullnessLifcycle; @Mock - private CaptioningManager mCaptioningManager; - @Mock private KeyguardManager mKeyguardManager; @Mock private ActivityManager mActivityManager; @@ -117,6 +116,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { when(mRingerModeLiveData.getValue()).thenReturn(-1); when(mRingerModeInternalLiveData.getValue()).thenReturn(-1); when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); + when(mUserTracker.getUserContext()).thenReturn(mContext); // Enable group volume adjustments mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions, @@ -127,7 +127,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, - mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mKeyguardManager, + mPackageManager, mWakefullnessLifcycle, mKeyguardManager, mActivityManager, mUserTracker, mDumpManager, mCallback); mVolumeController.setEnableDialogs(true, true); } @@ -219,6 +219,11 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { verify(mRingerModeInternalLiveData).observeForever(any()); } + @Test + public void testAddCallbackWithUserTracker() { + verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class)); + } + static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver; @@ -234,7 +239,6 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, - CaptioningManager captioningManager, KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, @@ -242,7 +246,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { C callback) { super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, - packageManager, wakefulnessLifecycle, captioningManager, keyguardManager, + packageManager, wakefulnessLifecycle, keyguardManager, activityManager, userTracker, dumpManager); mCallbacks = callback; diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index fa18e575220c..2828eb2a7b56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -31,6 +31,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -93,6 +94,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { View mDrawerVibrate; View mDrawerMute; View mDrawerNormal; + CaptionsToggleImageButton mODICaptionsIcon; + private TestableLooper mTestableLooper; private ConfigurationController mConfigurationController; private int mOriginalOrientation; @@ -180,6 +183,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate); mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute); mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal); + mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon); Prefs.putInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, @@ -688,6 +692,28 @@ public class VolumeDialogImplTest extends SysuiTestCase { assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); } + @Test + public void testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState() { + ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = + ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); + verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); + VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); + + callbacks.onCaptionEnabledStateChanged(true, true); + verify(mVolumeDialogController).setCaptionsEnabledState(eq(false)); + } + + @Test + public void testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue() { + ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = + ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); + verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); + VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); + + callbacks.onCaptionEnabledStateChanged(true, false); + assertTrue(mODICaptionsIcon.getCaptionsEnabled()); + } + /** * The content description should include ringer state, and the correct one. */ |