• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.accessibility;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.annotation.SystemService;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.res.Resources;
29 import android.database.ContentObserver;
30 import android.graphics.Color;
31 import android.graphics.Typeface;
32 import android.net.Uri;
33 import android.os.Handler;
34 import android.provider.Settings.Secure;
35 import android.text.TextUtils;
36 
37 import com.android.internal.R;
38 
39 import java.util.ArrayList;
40 import java.util.Locale;
41 
42 /**
43  * Contains methods for accessing and monitoring preferred video captioning state and visual
44  * properties.
45  */
46 @SystemService(Context.CAPTIONING_SERVICE)
47 public class CaptioningManager {
48     /** Default captioning enabled value. */
49     private static final int DEFAULT_ENABLED = 0;
50     private static final boolean SYSTEM_AUDIO_CAPTIONING_DEFAULT_ENABLED = false;
51 
52     /** Default style preset as an index into {@link CaptionStyle#PRESETS}. */
53     private static final int DEFAULT_PRESET = 0;
54 
55     /** Default scaling value for caption fonts. */
56     private static final float DEFAULT_FONT_SCALE = 1;
57 
58     private final ArrayList<CaptioningChangeListener> mListeners = new ArrayList<>();
59     private final ContentResolver mContentResolver;
60     private final ContentObserver mContentObserver;
61     private final Resources mResources;
62     private final Context mContext;
63     private final AccessibilityManager mAccessibilityManager;
64 
65     /**
66      * Creates a new captioning manager for the specified context.
67      *
68      * @hide
69      */
CaptioningManager(Context context)70     public CaptioningManager(Context context) {
71         mContext = context;
72         mContentResolver = context.getContentResolver();
73         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
74 
75         final Handler handler = new Handler(context.getMainLooper());
76         mContentObserver = new MyContentObserver(handler);
77         mResources = context.getResources();
78     }
79 
80     /**
81      * @return the user's preferred captioning enabled state
82      */
isEnabled()83     public final boolean isEnabled() {
84         return Secure.getInt(
85                 mContentResolver, Secure.ACCESSIBILITY_CAPTIONING_ENABLED, DEFAULT_ENABLED) == 1;
86     }
87 
88     /**
89      * @return the raw locale string for the user's preferred captioning
90      *         language
91      * @hide
92      */
93     @Nullable
getRawLocale()94     public final String getRawLocale() {
95         return Secure.getString(mContentResolver, Secure.ACCESSIBILITY_CAPTIONING_LOCALE);
96     }
97 
98     /**
99      * @return the locale for the user's preferred captioning language, or null
100      *         if not specified
101      */
102     @Nullable
getLocale()103     public final Locale getLocale() {
104         final String rawLocale = getRawLocale();
105         if (!TextUtils.isEmpty(rawLocale)) {
106             final String[] splitLocale = rawLocale.split("_");
107             switch (splitLocale.length) {
108                 case 3:
109                     return new Locale(splitLocale[0], splitLocale[1], splitLocale[2]);
110                 case 2:
111                     return new Locale(splitLocale[0], splitLocale[1]);
112                 case 1:
113                     return new Locale(splitLocale[0]);
114             }
115         }
116 
117         return null;
118     }
119 
120     /**
121      * @return the user's preferred font scaling factor for video captions, or 1 if not
122      *         specified
123      */
getFontScale()124     public final float getFontScale() {
125         return Secure.getFloat(
126                 mContentResolver, Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE, DEFAULT_FONT_SCALE);
127     }
128 
129     /**
130      * @return the raw preset number, or the first preset if not specified
131      * @hide
132      */
getRawUserStyle()133     public int getRawUserStyle() {
134         return Secure.getInt(
135                 mContentResolver, Secure.ACCESSIBILITY_CAPTIONING_PRESET, DEFAULT_PRESET);
136     }
137 
138     /**
139      * @return the user's preferred visual properties for captions as a
140      *         {@link CaptionStyle}, or the default style if not specified
141      */
142     @NonNull
getUserStyle()143     public CaptionStyle getUserStyle() {
144         final int preset = getRawUserStyle();
145         if (preset == CaptionStyle.PRESET_CUSTOM) {
146             return CaptionStyle.getCustomStyle(mContentResolver);
147         }
148 
149         return CaptionStyle.PRESETS[preset];
150     }
151 
152     /**
153      * @return the system audio caption enabled state.
154      */
isSystemAudioCaptioningEnabled()155     public final boolean isSystemAudioCaptioningEnabled() {
156         return Secure.getIntForUser(mContentResolver, Secure.ODI_CAPTIONS_ENABLED,
157                 SYSTEM_AUDIO_CAPTIONING_DEFAULT_ENABLED ? 1 : 0, mContext.getUserId()) == 1;
158     }
159 
160     /**
161      * Sets the system audio caption enabled state.
162      *
163      * @param isEnabled The system audio captioning enabled state.
164      *
165      * @throws SecurityException if the caller does not have permission
166      * {@link Manifest.permission#SET_SYSTEM_AUDIO_CAPTION}
167      *
168      * @hide
169      */
170     @SystemApi
171     @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
setSystemAudioCaptioningEnabled(boolean isEnabled)172     public final void setSystemAudioCaptioningEnabled(boolean isEnabled) {
173         if (mAccessibilityManager != null) {
174             mAccessibilityManager.setSystemAudioCaptioningEnabled(isEnabled,
175                     mContext.getUserId());
176         }
177     }
178 
179     /**
180      * @return the system audio caption UI enabled state.
181      */
isSystemAudioCaptioningUiEnabled()182     public final boolean isSystemAudioCaptioningUiEnabled() {
183         return mAccessibilityManager != null
184                 && mAccessibilityManager.isSystemAudioCaptioningUiEnabled(mContext.getUserId());
185     }
186 
187     /**
188      * Sets the system audio caption UI enabled state.
189      *
190      * @param isEnabled The system audio captioning UI enabled state.
191      *
192      * @throws SecurityException if the caller does not have permission
193      * {@link Manifest.permission#SET_SYSTEM_AUDIO_CAPTION}
194      *
195      * @hide
196      */
197     @SystemApi
198     @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
setSystemAudioCaptioningUiEnabled(boolean isEnabled)199     public final void setSystemAudioCaptioningUiEnabled(boolean isEnabled) {
200         if (mAccessibilityManager != null) {
201             mAccessibilityManager.setSystemAudioCaptioningUiEnabled(isEnabled,
202                     mContext.getUserId());
203         }
204     }
205 
206     /**
207      * Adds a listener for changes in the user's preferred captioning enabled
208      * state and visual properties.
209      *
210      * @param listener the listener to add
211      */
addCaptioningChangeListener(@onNull CaptioningChangeListener listener)212     public void addCaptioningChangeListener(@NonNull CaptioningChangeListener listener) {
213         synchronized (mListeners) {
214             if (mListeners.isEmpty()) {
215                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_ENABLED);
216                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR);
217                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR);
218                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR);
219                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE);
220                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR);
221                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE);
222                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE);
223                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_LOCALE);
224                 registerObserver(Secure.ACCESSIBILITY_CAPTIONING_PRESET);
225                 registerObserver(Secure.ODI_CAPTIONS_ENABLED);
226                 registerObserver(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED);
227             }
228 
229             mListeners.add(listener);
230         }
231     }
232 
registerObserver(String key)233     private void registerObserver(String key) {
234         mContentResolver.registerContentObserver(Secure.getUriFor(key), false, mContentObserver);
235     }
236 
237     /**
238      * Removes a listener previously added using
239      * {@link #addCaptioningChangeListener}.
240      *
241      * @param listener the listener to remove
242      */
removeCaptioningChangeListener(@onNull CaptioningChangeListener listener)243     public void removeCaptioningChangeListener(@NonNull CaptioningChangeListener listener) {
244         synchronized (mListeners) {
245             mListeners.remove(listener);
246 
247             if (mListeners.isEmpty()) {
248                 mContentResolver.unregisterContentObserver(mContentObserver);
249             }
250         }
251     }
252 
253     /**
254      * Returns true if system wide call captioning is enabled for this device.
255      */
isCallCaptioningEnabled()256     public boolean isCallCaptioningEnabled() {
257         try {
258             return mResources.getBoolean(
259                 R.bool.config_systemCaptionsServiceCallsEnabled);
260         } catch (Resources.NotFoundException e) {
261             // The resource may not be defined, return false in that case
262             return false;
263         }
264     }
265 
notifyEnabledChanged()266     private void notifyEnabledChanged() {
267         final boolean enabled = isEnabled();
268         synchronized (mListeners) {
269             for (CaptioningChangeListener listener : mListeners) {
270                 listener.onEnabledChanged(enabled);
271             }
272         }
273     }
274 
notifyUserStyleChanged()275     private void notifyUserStyleChanged() {
276         final CaptionStyle userStyle = getUserStyle();
277         synchronized (mListeners) {
278             for (CaptioningChangeListener listener : mListeners) {
279                 listener.onUserStyleChanged(userStyle);
280             }
281         }
282     }
283 
notifyLocaleChanged()284     private void notifyLocaleChanged() {
285         final Locale locale = getLocale();
286         synchronized (mListeners) {
287             for (CaptioningChangeListener listener : mListeners) {
288                 listener.onLocaleChanged(locale);
289             }
290         }
291     }
292 
notifyFontScaleChanged()293     private void notifyFontScaleChanged() {
294         final float fontScale = getFontScale();
295         synchronized (mListeners) {
296             for (CaptioningChangeListener listener : mListeners) {
297                 listener.onFontScaleChanged(fontScale);
298             }
299         }
300     }
301 
notifySystemAudioCaptionChanged()302     private void notifySystemAudioCaptionChanged() {
303         final boolean enabled = isSystemAudioCaptioningEnabled();
304         synchronized (mListeners) {
305             for (CaptioningChangeListener listener : mListeners) {
306                 listener.onSystemAudioCaptioningChanged(enabled);
307             }
308         }
309     }
310 
notifySystemAudioCaptionUiChanged()311     private void notifySystemAudioCaptionUiChanged() {
312         final boolean enabled = isSystemAudioCaptioningUiEnabled();
313         synchronized (mListeners) {
314             for (CaptioningChangeListener listener : mListeners) {
315                 listener.onSystemAudioCaptioningUiChanged(enabled);
316             }
317         }
318     }
319 
320     private class MyContentObserver extends ContentObserver {
321         private final Handler mHandler;
322 
MyContentObserver(Handler handler)323         public MyContentObserver(Handler handler) {
324             super(handler);
325 
326             mHandler = handler;
327         }
328 
329         @Override
onChange(boolean selfChange, Uri uri)330         public void onChange(boolean selfChange, Uri uri) {
331             final String uriPath = uri.getPath();
332             final String name = uriPath.substring(uriPath.lastIndexOf('/') + 1);
333             if (Secure.ACCESSIBILITY_CAPTIONING_ENABLED.equals(name)) {
334                 notifyEnabledChanged();
335             } else if (Secure.ACCESSIBILITY_CAPTIONING_LOCALE.equals(name)) {
336                 notifyLocaleChanged();
337             } else if (Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE.equals(name)) {
338                 notifyFontScaleChanged();
339             } else if (Secure.ODI_CAPTIONS_ENABLED.equals(name)) {
340                 notifySystemAudioCaptionChanged();
341             } else if (Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED.equals(name)) {
342                 notifySystemAudioCaptionUiChanged();
343             } else {
344                 // We only need a single callback when multiple style properties
345                 // change in rapid succession.
346                 mHandler.removeCallbacks(mStyleChangedRunnable);
347                 mHandler.post(mStyleChangedRunnable);
348             }
349         }
350     };
351 
352     /**
353      * Runnable posted when user style properties change. This is used to
354      * prevent unnecessary change notifications when multiple properties change
355      * in rapid succession.
356      */
357     private final Runnable mStyleChangedRunnable = new Runnable() {
358         @Override
359         public void run() {
360             notifyUserStyleChanged();
361         }
362     };
363 
364     /**
365      * Specifies visual properties for video captions, including foreground and
366      * background colors, edge properties, and typeface.
367      */
368     public static final class CaptionStyle {
369         /**
370          * Packed value for a color of 'none' and a cached opacity of 100%.
371          *
372          * @hide
373          */
374         private static final int COLOR_NONE_OPAQUE = 0x000000FF;
375 
376         /**
377          * Packed value for a color of 'default' and opacity of 100%.
378          *
379          * @hide
380          */
381         public static final int COLOR_UNSPECIFIED = 0x00FFFFFF;
382 
383         private static final CaptionStyle WHITE_ON_BLACK;
384         private static final CaptionStyle BLACK_ON_WHITE;
385         private static final CaptionStyle YELLOW_ON_BLACK;
386         private static final CaptionStyle YELLOW_ON_BLUE;
387         private static final CaptionStyle DEFAULT_CUSTOM;
388         private static final CaptionStyle UNSPECIFIED;
389 
390         /** The default caption style used to fill in unspecified values. @hide */
391         public static final CaptionStyle DEFAULT;
392 
393         /** @hide */
394         @UnsupportedAppUsage
395         public static final CaptionStyle[] PRESETS;
396 
397         /** @hide */
398         public static final int PRESET_CUSTOM = -1;
399 
400         /** Unspecified edge type value. */
401         public static final int EDGE_TYPE_UNSPECIFIED = -1;
402 
403         /** Edge type value specifying no character edges. */
404         public static final int EDGE_TYPE_NONE = 0;
405 
406         /** Edge type value specifying uniformly outlined character edges. */
407         public static final int EDGE_TYPE_OUTLINE = 1;
408 
409         /** Edge type value specifying drop-shadowed character edges. */
410         public static final int EDGE_TYPE_DROP_SHADOW = 2;
411 
412         /** Edge type value specifying raised bevel character edges. */
413         public static final int EDGE_TYPE_RAISED = 3;
414 
415         /** Edge type value specifying depressed bevel character edges. */
416         public static final int EDGE_TYPE_DEPRESSED = 4;
417 
418         /** The preferred foreground color for video captions. */
419         public final int foregroundColor;
420 
421         /** The preferred background color for video captions. */
422         public final int backgroundColor;
423 
424         /**
425          * The preferred edge type for video captions, one of:
426          * <ul>
427          * <li>{@link #EDGE_TYPE_UNSPECIFIED}
428          * <li>{@link #EDGE_TYPE_NONE}
429          * <li>{@link #EDGE_TYPE_OUTLINE}
430          * <li>{@link #EDGE_TYPE_DROP_SHADOW}
431          * <li>{@link #EDGE_TYPE_RAISED}
432          * <li>{@link #EDGE_TYPE_DEPRESSED}
433          * </ul>
434          */
435         public final int edgeType;
436 
437         /**
438          * The preferred edge color for video captions, if using an edge type
439          * other than {@link #EDGE_TYPE_NONE}.
440          */
441         public final int edgeColor;
442 
443         /** The preferred window color for video captions. */
444         public final int windowColor;
445 
446         /**
447          * @hide
448          */
449         public final String mRawTypeface;
450 
451         private final boolean mHasForegroundColor;
452         private final boolean mHasBackgroundColor;
453         private final boolean mHasEdgeType;
454         private final boolean mHasEdgeColor;
455         private final boolean mHasWindowColor;
456 
457         /** Lazily-created typeface based on the raw typeface string. */
458         private Typeface mParsedTypeface;
459 
CaptionStyle(int foregroundColor, int backgroundColor, int edgeType, int edgeColor, int windowColor, String rawTypeface)460         private CaptionStyle(int foregroundColor, int backgroundColor, int edgeType, int edgeColor,
461                 int windowColor, String rawTypeface) {
462             mHasForegroundColor = hasColor(foregroundColor);
463             mHasBackgroundColor = hasColor(backgroundColor);
464             mHasEdgeType = edgeType != EDGE_TYPE_UNSPECIFIED;
465             mHasEdgeColor = hasColor(edgeColor);
466             mHasWindowColor = hasColor(windowColor);
467 
468             // Always use valid colors, even when no override is specified, to
469             // ensure backwards compatibility with apps targeting KitKat MR2.
470             this.foregroundColor = mHasForegroundColor ? foregroundColor : Color.WHITE;
471             this.backgroundColor = mHasBackgroundColor ? backgroundColor : Color.BLACK;
472             this.edgeType = mHasEdgeType ? edgeType : EDGE_TYPE_NONE;
473             this.edgeColor = mHasEdgeColor ? edgeColor : Color.BLACK;
474             this.windowColor = mHasWindowColor ? windowColor : COLOR_NONE_OPAQUE;
475 
476             mRawTypeface = rawTypeface;
477         }
478 
479         /**
480          * Returns whether a packed color indicates a non-default value.
481          *
482          * @param packedColor the packed color value
483          * @return {@code true} if a non-default value is specified
484          * @hide
485          */
hasColor(int packedColor)486         public static boolean hasColor(int packedColor) {
487             // Matches the color packing code from Settings. "Default" packed
488             // colors are indicated by zero alpha and non-zero red/blue. The
489             // cached alpha value used by Settings is stored in green.
490             return (packedColor >>> 24) != 0 || (packedColor & 0xFFFF00) == 0;
491         }
492 
493         /**
494          * Applies a caption style, overriding any properties that are specified
495          * in the overlay caption.
496          *
497          * @param overlay The style to apply
498          * @return A caption style with the overlay style applied
499          * @hide
500          */
501         @NonNull
applyStyle(@onNull CaptionStyle overlay)502         public CaptionStyle applyStyle(@NonNull CaptionStyle overlay) {
503             final int newForegroundColor = overlay.hasForegroundColor() ?
504                     overlay.foregroundColor : foregroundColor;
505             final int newBackgroundColor = overlay.hasBackgroundColor() ?
506                     overlay.backgroundColor : backgroundColor;
507             final int newEdgeType = overlay.hasEdgeType() ?
508                     overlay.edgeType : edgeType;
509             final int newEdgeColor = overlay.hasEdgeColor() ?
510                     overlay.edgeColor : edgeColor;
511             final int newWindowColor = overlay.hasWindowColor() ?
512                     overlay.windowColor : windowColor;
513             final String newRawTypeface = overlay.mRawTypeface != null ?
514                     overlay.mRawTypeface : mRawTypeface;
515             return new CaptionStyle(newForegroundColor, newBackgroundColor, newEdgeType,
516                     newEdgeColor, newWindowColor, newRawTypeface);
517         }
518 
519         /**
520          * @return {@code true} if the user has specified a background color
521          *         that should override the application default, {@code false}
522          *         otherwise
523          */
hasBackgroundColor()524         public boolean hasBackgroundColor() {
525             return mHasBackgroundColor;
526         }
527 
528         /**
529          * @return {@code true} if the user has specified a foreground color
530          *         that should override the application default, {@code false}
531          *         otherwise
532          */
hasForegroundColor()533         public boolean hasForegroundColor() {
534             return mHasForegroundColor;
535         }
536 
537         /**
538          * @return {@code true} if the user has specified an edge type that
539          *         should override the application default, {@code false}
540          *         otherwise
541          */
hasEdgeType()542         public boolean hasEdgeType() {
543             return mHasEdgeType;
544         }
545 
546         /**
547          * @return {@code true} if the user has specified an edge color that
548          *         should override the application default, {@code false}
549          *         otherwise
550          */
hasEdgeColor()551         public boolean hasEdgeColor() {
552             return mHasEdgeColor;
553         }
554 
555         /**
556          * @return {@code true} if the user has specified a window color that
557          *         should override the application default, {@code false}
558          *         otherwise
559          */
hasWindowColor()560         public boolean hasWindowColor() {
561             return mHasWindowColor;
562         }
563 
564         /**
565          * @return the preferred {@link Typeface} for video captions, or null if
566          *         not specified
567          */
568         @Nullable
getTypeface()569         public Typeface getTypeface() {
570             if (mParsedTypeface == null && !TextUtils.isEmpty(mRawTypeface)) {
571                 mParsedTypeface = Typeface.create(mRawTypeface, Typeface.NORMAL);
572             }
573             return mParsedTypeface;
574         }
575 
576         /**
577          * @hide
578          */
579         @NonNull
getCustomStyle(ContentResolver cr)580         public static CaptionStyle getCustomStyle(ContentResolver cr) {
581             final CaptionStyle defStyle = CaptionStyle.DEFAULT_CUSTOM;
582             final int foregroundColor = Secure.getInt(
583                     cr, Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR, defStyle.foregroundColor);
584             final int backgroundColor = Secure.getInt(
585                     cr, Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, defStyle.backgroundColor);
586             final int edgeType = Secure.getInt(
587                     cr, Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE, defStyle.edgeType);
588             final int edgeColor = Secure.getInt(
589                     cr, Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR, defStyle.edgeColor);
590             final int windowColor = Secure.getInt(
591                     cr, Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, defStyle.windowColor);
592 
593             String rawTypeface = Secure.getString(cr, Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE);
594             if (rawTypeface == null) {
595                 rawTypeface = defStyle.mRawTypeface;
596             }
597 
598             return new CaptionStyle(foregroundColor, backgroundColor, edgeType, edgeColor,
599                     windowColor, rawTypeface);
600         }
601 
602         static {
603             WHITE_ON_BLACK = new CaptionStyle(Color.WHITE, Color.BLACK, EDGE_TYPE_NONE,
604                     Color.BLACK, COLOR_NONE_OPAQUE, null);
605             BLACK_ON_WHITE = new CaptionStyle(Color.BLACK, Color.WHITE, EDGE_TYPE_NONE,
606                     Color.BLACK, COLOR_NONE_OPAQUE, null);
607             YELLOW_ON_BLACK = new CaptionStyle(Color.YELLOW, Color.BLACK, EDGE_TYPE_NONE,
608                     Color.BLACK, COLOR_NONE_OPAQUE, null);
609             YELLOW_ON_BLUE = new CaptionStyle(Color.YELLOW, Color.BLUE, EDGE_TYPE_NONE,
610                     Color.BLACK, COLOR_NONE_OPAQUE, null);
611             UNSPECIFIED = new CaptionStyle(COLOR_UNSPECIFIED, COLOR_UNSPECIFIED,
612                     EDGE_TYPE_UNSPECIFIED, COLOR_UNSPECIFIED, COLOR_UNSPECIFIED, null);
613 
614             // The ordering of these cannot change since we store the index
615             // directly in preferences.
616             PRESETS = new CaptionStyle[] {
617                     WHITE_ON_BLACK, BLACK_ON_WHITE, YELLOW_ON_BLACK, YELLOW_ON_BLUE, UNSPECIFIED
618             };
619 
620             DEFAULT_CUSTOM = WHITE_ON_BLACK;
621             DEFAULT = WHITE_ON_BLACK;
622         }
623     }
624 
625     /**
626      * Listener for changes in captioning properties, including enabled state
627      * and user style preferences.
628      */
629     public static abstract class CaptioningChangeListener {
630         /**
631          * Called when the captioning enabled state changes.
632          *
633          * @param enabled the user's new preferred captioning enabled state
634          */
onEnabledChanged(boolean enabled)635         public void onEnabledChanged(boolean enabled) {}
636 
637         /**
638          * Called when the captioning user style changes.
639          *
640          * @param userStyle the user's new preferred style
641          * @see CaptioningManager#getUserStyle()
642          */
onUserStyleChanged(@onNull CaptionStyle userStyle)643         public void onUserStyleChanged(@NonNull CaptionStyle userStyle) {}
644 
645         /**
646          * Called when the captioning locale changes.
647          *
648          * @param locale the preferred captioning locale, or {@code null} if not specified
649          * @see CaptioningManager#getLocale()
650          */
onLocaleChanged(@ullable Locale locale)651         public void onLocaleChanged(@Nullable Locale locale) {}
652 
653         /**
654          * Called when the captioning font scaling factor changes.
655          *
656          * @param fontScale the preferred font scaling factor
657          * @see CaptioningManager#getFontScale()
658          */
onFontScaleChanged(float fontScale)659         public void onFontScaleChanged(float fontScale) {}
660 
661 
662         /**
663          * Called when the system audio caption enabled state changes.
664          *
665          * @param enabled the system audio caption enabled state
666          */
onSystemAudioCaptioningChanged(boolean enabled)667         public void onSystemAudioCaptioningChanged(boolean enabled) {}
668 
669         /**
670          * Called when the system audio caption UI enabled state changes.
671          *
672          * @param enabled the system audio caption UI enabled state
673          */
onSystemAudioCaptioningUiChanged(boolean enabled)674         public void onSystemAudioCaptioningUiChanged(boolean enabled) {}
675     }
676 
677     /**
678      * Interface for accessing the system audio captioning related secure setting keys.
679      *
680      * @hide
681      */
682     public interface SystemAudioCaptioningAccessing {
683         /**
684          * Sets the system audio caption enabled state.
685          *
686          * @param isEnabled The system audio captioning enabled state.
687          * @param userId The user Id.
688          */
setSystemAudioCaptioningEnabled(boolean isEnabled, int userId)689         void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId);
690 
691         /**
692          * Gets the system audio caption UI enabled state.
693          *
694          * @param userId The user Id.
695          * @return the system audio caption UI enabled state.
696          */
isSystemAudioCaptioningUiEnabled(int userId)697         boolean isSystemAudioCaptioningUiEnabled(int userId);
698 
699         /**
700          * Sets the system audio caption UI enabled state.
701          *
702          * @param isEnabled The system audio captioning UI enabled state.
703          * @param userId The user Id.
704          */
setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId)705         void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId);
706     }
707 }
708