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 {