diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4836e6c0cba7a..b437435902dfe 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5589,6 +5589,15 @@ public final class Settings { private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR; + /** + * Setting to indicate that on device captions are enabled. + * + * @hide + */ + public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled"; + + private static final Validator ODI_CAPTIONS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * On Android 8.0 (API level 26) and higher versions of the platform, * a 64-bit number (expressed as a hexadecimal string), unique to @@ -8950,6 +8959,7 @@ public final class Settings { VALIDATORS.put(SILENCE_TIMER_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR); } /** diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bfb6d247e0dda..dce144a64266a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3513,6 +3513,12 @@ --> + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml b/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml new file mode 100644 index 0000000000000..f3d8d3b9c2e24 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index ca34c23eca1f8..130be89914a80 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -98,5 +98,27 @@ + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c83cb59fa7927..b42e5dd335001 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -336,6 +336,8 @@ 64dp + 64dp + 48dp 4dp diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index 1596ddbafda0d..6e740b8536b3e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -55,6 +55,7 @@ public class Events { public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode) public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool) public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool) + public static final int EVENT_ODI_CAPTIONS_CLICK = 21; private static final String[] EVENT_TAGS = { "show_dialog", @@ -77,7 +78,8 @@ public class Events { "zen_mode_config_changed", "ringer_toggle", "show_usb_overheat_alarm", - "dismiss_usb_overheat_alarm" + "dismiss_usb_overheat_alarm", + "odi_captions_click" }; public static final int DISMISS_REASON_UNKNOWN = 0; @@ -90,6 +92,7 @@ public class Events { public static final int DISMISS_STREAM_GONE = 7; public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8; public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9; + public static final int DISMISS_REASON_ODI_CAPTIONS_CLICKED = 10; public static final String[] DISMISS_REASONS = { "unknown", "touch_outside", @@ -100,7 +103,8 @@ public class Events { "done_clicked", "a11y_stream_changed", "output_chooser", - "usb_temperature_below_threshold" + "usb_temperature_below_threshold", + "odi_captions_clicked" }; public static final int SHOW_REASON_UNKNOWN = 0; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 4c16297154f37..9192a25dcd3b9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -48,6 +48,7 @@ import android.os.Vibrator; import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.view.accessibility.AccessibilityManager; @@ -270,6 +271,22 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mWorker.sendEmptyMessage(W.GET_STATE); } + public boolean areCaptionsEnabled() { + int currentValue = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ODI_CAPTIONS_ENABLED, 0); + return currentValue == 1; + } + + public void setCaptionsEnabled(boolean isEnabled) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0); + } + + public void getCaptionsComponentState() { + if (mDestroyed) return; + mWorker.sendEmptyMessage(W.GET_CAPTIONS_COMPONENT_STATE); + } + public void notifyVisible(boolean visible) { if (mDestroyed) return; mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget(); @@ -365,6 +382,38 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } } + private void onGetCaptionsComponentStateW() { + try { + String componentNameString = mContext.getString( + com.android.internal.R.string.config_defaultSystemCaptionsService); + if (TextUtils.isEmpty(componentNameString)) { + // component doesn't exist + mCallbacks.onCaptionComponentStateChanged(false); + return; + } + + if (D.BUG) { + Log.i(TAG, String.format( + "isCaptionsServiceEnabled componentNameString=%s", componentNameString)); + } + + ComponentName componentName = ComponentName.unflattenFromString(componentNameString); + if (componentName == null) { + mCallbacks.onCaptionComponentStateChanged(false); + return; + } + + PackageManager packageManager = mContext.getPackageManager(); + mCallbacks.onCaptionComponentStateChanged( + packageManager.getComponentEnabledSetting(componentName) + == PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + } catch (Exception ex) { + Log.e(TAG, + "isCaptionsServiceEnabled failed to check for captions component", ex); + mCallbacks.onCaptionComponentStateChanged(false); + } + } + private void onAccessibilityModeChanged(Boolean showA11yStream) { mCallbacks.onAccessibilityModeChanged(showA11yStream); } @@ -718,6 +767,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final int USER_ACTIVITY = 13; private static final int SHOW_SAFETY_WARNING = 14; private static final int ACCESSIBILITY_MODE_CHANGED = 15; + private static final int GET_CAPTIONS_COMPONENT_STATE = 16; W(Looper looper) { super(looper); @@ -740,8 +790,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break; case USER_ACTIVITY: onUserActivityW(); break; case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break; + case GET_CAPTIONS_COMPONENT_STATE: onGetCaptionsComponentStateW(); break; case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj); - } } } @@ -881,6 +931,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa }); } } + + @Override + public void onCaptionComponentStateChanged(Boolean isComponentEnabled) { + boolean componentEnabled = isComponentEnabled == null ? false : isComponentEnabled; + for (final Map.Entry entry : mCallbackMap.entrySet()) { + entry.getValue().post( + () -> entry.getKey().onCaptionComponentStateChanged(componentEnabled)); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index ffd8206ca8879..398b30963da75 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -29,6 +29,7 @@ import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static com.android.systemui.volume.Events.DISMISS_REASON_ODI_CAPTIONS_CLICKED; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; import android.animation.ObjectAnimator; @@ -125,6 +126,8 @@ public class VolumeDialogImpl implements VolumeDialog { private ViewGroup mDialogRowsView; private ViewGroup mRinger; private ImageButton mRingerIcon; + private ViewGroup mODICaptionsView; + private ImageButton mODICaptionsIcon; private View mSettingsView; private ImageButton mSettingsIcon; private FrameLayout mZenIcon; @@ -240,6 +243,10 @@ public class VolumeDialogImpl implements VolumeDialog { mRingerIcon = mRinger.findViewById(R.id.ringer_icon); mZenIcon = mRinger.findViewById(R.id.dnd_icon); } + mODICaptionsView = mDialog.findViewById(R.id.odi_captions); + if (mODICaptionsView != null) { + mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon); + } mSettingsView = mDialog.findViewById(R.id.settings_container); mSettingsIcon = mDialog.findViewById(R.id.settings); @@ -270,6 +277,7 @@ public class VolumeDialogImpl implements VolumeDialog { updateRowsH(getActiveRow()); initRingerH(); initSettingsH(); + initODICaptionsH(); } protected ViewGroup getDialogView() { @@ -478,6 +486,42 @@ public class VolumeDialogImpl implements VolumeDialog { updateRingerH(); } + private void initODICaptionsH() { + if (mODICaptionsIcon != null) { + mODICaptionsIcon.setOnClickListener(v -> { + onCaptionIconClicked(); + Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK); + dismissH(DISMISS_REASON_ODI_CAPTIONS_CLICKED); + }); + } + + mController.getCaptionsComponentState(); + } + + private void updateODICaptionsH(boolean isServiceComponentEnabled) { + if (mODICaptionsView != null) { + mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE); + } + + if (!isServiceComponentEnabled) return; + + updateCaptionsIcon(); + } + + private void updateCaptionsIcon() { + mHandler.post( + mODICaptionsIcon.setImageResourceAsync( + mController.areCaptionsEnabled() + ? R.drawable.ic_volume_odi_captions + : R.drawable.ic_volume_odi_captions_disabled)); + } + + private void onCaptionIconClicked() { + boolean isEnabled = mController.areCaptionsEnabled(); + mController.setCaptionsEnabled(!isEnabled); + updateCaptionsIcon(); + } + private void incrementManualToggleCount() { ContentResolver cr = mContext.getContentResolver(); int ringerCount = Settings.Secure.getInt(cr, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, 0); @@ -558,6 +602,7 @@ public class VolumeDialogImpl implements VolumeDialog { mDialog.show(); Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); mController.notifyVisible(true); + mController.getCaptionsComponentState(); } protected void rescheduleTimeoutH() { @@ -1151,6 +1196,11 @@ public class VolumeDialogImpl implements VolumeDialog { } } + + @Override + public void onCaptionComponentStateChanged(Boolean isComponentEnabled) { + updateODICaptionsH(isComponentEnabled); + } }; private final class H extends Handler {