• 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 com.android.settings.accessibility;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Color;
23 import android.os.Bundle;
24 import android.preference.PreferenceFrameLayout;
25 import android.provider.Settings;
26 import android.support.v7.preference.ListPreference;
27 import android.support.v7.preference.Preference;
28 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
29 import android.support.v7.preference.PreferenceCategory;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.View.OnLayoutChangeListener;
33 import android.view.ViewGroup;
34 import android.view.ViewGroup.LayoutParams;
35 import android.view.accessibility.CaptioningManager;
36 import android.view.accessibility.CaptioningManager.CaptionStyle;
37 
38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
39 import com.android.internal.widget.SubtitleView;
40 import com.android.settings.R;
41 import com.android.settings.SettingsActivity;
42 import com.android.settings.SettingsPreferenceFragment;
43 import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
44 import com.android.settings.widget.SwitchBar;
45 import com.android.settings.widget.ToggleSwitch;
46 import com.android.settings.widget.ToggleSwitch.OnBeforeCheckedChangeListener;
47 import com.android.settingslib.accessibility.AccessibilityUtils;
48 
49 import java.util.Locale;
50 
51 /**
52  * Settings fragment containing captioning properties.
53  */
54 public class CaptionPropertiesFragment extends SettingsPreferenceFragment
55         implements OnPreferenceChangeListener, OnValueChangedListener {
56     private static final String PREF_BACKGROUND_COLOR = "captioning_background_color";
57     private static final String PREF_BACKGROUND_OPACITY = "captioning_background_opacity";
58     private static final String PREF_FOREGROUND_COLOR = "captioning_foreground_color";
59     private static final String PREF_FOREGROUND_OPACITY = "captioning_foreground_opacity";
60     private static final String PREF_WINDOW_COLOR = "captioning_window_color";
61     private static final String PREF_WINDOW_OPACITY = "captioning_window_opacity";
62     private static final String PREF_EDGE_COLOR = "captioning_edge_color";
63     private static final String PREF_EDGE_TYPE = "captioning_edge_type";
64     private static final String PREF_FONT_SIZE = "captioning_font_size";
65     private static final String PREF_TYPEFACE = "captioning_typeface";
66     private static final String PREF_LOCALE = "captioning_locale";
67     private static final String PREF_PRESET = "captioning_preset";
68     private static final String PREF_CUSTOM = "custom";
69 
70     /** WebVtt specifies line height as 5.3% of the viewport height. */
71     private static final float LINE_HEIGHT_RATIO = 0.0533f;
72 
73     private CaptioningManager mCaptioningManager;
74     private SubtitleView mPreviewText;
75     private View mPreviewWindow;
76     private View mPreviewViewport;
77     private SwitchBar mSwitchBar;
78     private ToggleSwitch mToggleSwitch;
79 
80     // Standard options.
81     private LocalePreference mLocale;
82     private ListPreference mFontSize;
83     private PresetPreference mPreset;
84 
85     // Custom options.
86     private ListPreference mTypeface;
87     private ColorPreference mForegroundColor;
88     private ColorPreference mForegroundOpacity;
89     private EdgeTypePreference mEdgeType;
90     private ColorPreference mEdgeColor;
91     private ColorPreference mBackgroundColor;
92     private ColorPreference mBackgroundOpacity;
93     private ColorPreference mWindowColor;
94     private ColorPreference mWindowOpacity;
95     private PreferenceCategory mCustom;
96 
97     private boolean mShowingCustom;
98 
99     @Override
getMetricsCategory()100     public int getMetricsCategory() {
101         return MetricsEvent.ACCESSIBILITY_CAPTION_PROPERTIES;
102     }
103 
104     @Override
onCreate(Bundle icicle)105     public void onCreate(Bundle icicle) {
106         super.onCreate(icicle);
107 
108         mCaptioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
109 
110         addPreferencesFromResource(R.xml.captioning_settings);
111         initializeAllPreferences();
112         updateAllPreferences();
113         refreshShowingCustom();
114         installUpdateListeners();
115     }
116 
117     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)118     public View onCreateView(
119             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
120         final View rootView = inflater.inflate(R.layout.captioning_preview, container, false);
121 
122         // We have to do this now because PreferenceFrameLayout looks at it
123         // only when the view is added.
124         if (container instanceof PreferenceFrameLayout) {
125             ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
126         }
127 
128         final View content = super.onCreateView(inflater, container, savedInstanceState);
129         ((ViewGroup) rootView.findViewById(R.id.properties_fragment)).addView(
130                 content, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
131 
132         return rootView;
133     }
134 
135     @Override
onViewCreated(View view, Bundle savedInstanceState)136     public void onViewCreated(View view, Bundle savedInstanceState) {
137         super.onViewCreated(view, savedInstanceState);
138 
139         final boolean enabled = mCaptioningManager.isEnabled();
140         mPreviewText = (SubtitleView) view.findViewById(R.id.preview_text);
141         mPreviewText.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
142 
143         mPreviewWindow = view.findViewById(R.id.preview_window);
144         mPreviewViewport = view.findViewById(R.id.preview_viewport);
145         mPreviewViewport.addOnLayoutChangeListener(new OnLayoutChangeListener() {
146             @Override
147             public void onLayoutChange(View v, int left, int top, int right, int bottom,
148                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
149                 refreshPreviewText();
150             }
151         });
152     }
153 
154     @Override
onActivityCreated(Bundle savedInstanceState)155     public void onActivityCreated(Bundle savedInstanceState) {
156         super.onActivityCreated(savedInstanceState);
157 
158         final boolean enabled = mCaptioningManager.isEnabled();
159         SettingsActivity activity = (SettingsActivity) getActivity();
160         mSwitchBar = activity.getSwitchBar();
161         mSwitchBar.setSwitchBarText(R.string.accessibility_caption_master_switch_title,
162                 R.string.accessibility_caption_master_switch_title);
163         mSwitchBar.setCheckedInternal(enabled);
164         mToggleSwitch = mSwitchBar.getSwitch();
165 
166         getPreferenceScreen().setEnabled(enabled);
167 
168         refreshPreviewText();
169 
170         installSwitchBarToggleSwitch();
171     }
172 
173     @Override
onDestroyView()174     public void onDestroyView() {
175         super.onDestroyView();
176         removeSwitchBarToggleSwitch();
177     }
178 
refreshPreviewText()179     private void refreshPreviewText() {
180         final Context context = getActivity();
181         if (context == null) {
182             // We've been destroyed, abort!
183             return;
184         }
185 
186         final SubtitleView preview = mPreviewText;
187         if (preview != null) {
188             final int styleId = mCaptioningManager.getRawUserStyle();
189             applyCaptionProperties(mCaptioningManager, preview, mPreviewViewport, styleId);
190 
191             final Locale locale = mCaptioningManager.getLocale();
192             if (locale != null) {
193                 final CharSequence localizedText = AccessibilityUtils.getTextForLocale(
194                         context, locale, R.string.captioning_preview_text);
195                 preview.setText(localizedText);
196             } else {
197                 preview.setText(R.string.captioning_preview_text);
198             }
199 
200             final CaptionStyle style = mCaptioningManager.getUserStyle();
201             if (style.hasWindowColor()) {
202                 mPreviewWindow.setBackgroundColor(style.windowColor);
203             } else {
204                 final CaptionStyle defStyle = CaptionStyle.DEFAULT;
205                 mPreviewWindow.setBackgroundColor(defStyle.windowColor);
206             }
207         }
208     }
209 
applyCaptionProperties(CaptioningManager manager, SubtitleView previewText, View previewWindow, int styleId)210     public static void applyCaptionProperties(CaptioningManager manager, SubtitleView previewText,
211             View previewWindow, int styleId) {
212         previewText.setStyle(styleId);
213 
214         final Context context = previewText.getContext();
215         final ContentResolver cr = context.getContentResolver();
216         final float fontScale = manager.getFontScale();
217         if (previewWindow != null) {
218             // Assume the viewport is clipped with a 16:9 aspect ratio.
219             final float virtualHeight = Math.max(9 * previewWindow.getWidth(),
220                     16 * previewWindow.getHeight()) / 16.0f;
221             previewText.setTextSize(virtualHeight * LINE_HEIGHT_RATIO * fontScale);
222         } else {
223             final float textSize = context.getResources().getDimension(
224                     R.dimen.caption_preview_text_size);
225             previewText.setTextSize(textSize * fontScale);
226         }
227 
228         final Locale locale = manager.getLocale();
229         if (locale != null) {
230             final CharSequence localizedText = AccessibilityUtils.getTextForLocale(
231                     context, locale, R.string.captioning_preview_characters);
232             previewText.setText(localizedText);
233         } else {
234             previewText.setText(R.string.captioning_preview_characters);
235         }
236     }
237 
onInstallSwitchBarToggleSwitch()238     protected void onInstallSwitchBarToggleSwitch() {
239         mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() {
240             @Override
241             public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
242                 mSwitchBar.setCheckedInternal(checked);
243                 Settings.Secure.putInt(getActivity().getContentResolver(),
244                         Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, checked ? 1 : 0);
245                 getPreferenceScreen().setEnabled(checked);
246                 if (mPreviewText != null) {
247                     mPreviewText.setVisibility(checked ? View.VISIBLE : View.INVISIBLE);
248                 }
249                 return false;
250             }
251         });
252     }
253 
installSwitchBarToggleSwitch()254     private void installSwitchBarToggleSwitch() {
255         onInstallSwitchBarToggleSwitch();
256         mSwitchBar.show();
257     }
258 
removeSwitchBarToggleSwitch()259     private void removeSwitchBarToggleSwitch() {
260         mSwitchBar.hide();
261         mToggleSwitch.setOnBeforeCheckedChangeListener(null);
262     }
263 
initializeAllPreferences()264     private void initializeAllPreferences() {
265         mLocale = (LocalePreference) findPreference(PREF_LOCALE);
266         mFontSize = (ListPreference) findPreference(PREF_FONT_SIZE);
267 
268         final Resources res = getResources();
269         final int[] presetValues = res.getIntArray(R.array.captioning_preset_selector_values);
270         final String[] presetTitles = res.getStringArray(R.array.captioning_preset_selector_titles);
271         mPreset = (PresetPreference) findPreference(PREF_PRESET);
272         mPreset.setValues(presetValues);
273         mPreset.setTitles(presetTitles);
274 
275         mCustom = (PreferenceCategory) findPreference(PREF_CUSTOM);
276         mShowingCustom = true;
277 
278         final int[] colorValues = res.getIntArray(R.array.captioning_color_selector_values);
279         final String[] colorTitles = res.getStringArray(R.array.captioning_color_selector_titles);
280         mForegroundColor = (ColorPreference) mCustom.findPreference(PREF_FOREGROUND_COLOR);
281         mForegroundColor.setTitles(colorTitles);
282         mForegroundColor.setValues(colorValues);
283 
284         final int[] opacityValues = res.getIntArray(R.array.captioning_opacity_selector_values);
285         final String[] opacityTitles = res.getStringArray(
286                 R.array.captioning_opacity_selector_titles);
287         mForegroundOpacity = (ColorPreference) mCustom.findPreference(PREF_FOREGROUND_OPACITY);
288         mForegroundOpacity.setTitles(opacityTitles);
289         mForegroundOpacity.setValues(opacityValues);
290 
291         mEdgeColor = (ColorPreference) mCustom.findPreference(PREF_EDGE_COLOR);
292         mEdgeColor.setTitles(colorTitles);
293         mEdgeColor.setValues(colorValues);
294 
295         // Add "none" as an additional option for backgrounds.
296         final int[] bgColorValues = new int[colorValues.length + 1];
297         final String[] bgColorTitles = new String[colorTitles.length + 1];
298         System.arraycopy(colorValues, 0, bgColorValues, 1, colorValues.length);
299         System.arraycopy(colorTitles, 0, bgColorTitles, 1, colorTitles.length);
300         bgColorValues[0] = Color.TRANSPARENT;
301         bgColorTitles[0] = getString(R.string.color_none);
302         mBackgroundColor = (ColorPreference) mCustom.findPreference(PREF_BACKGROUND_COLOR);
303         mBackgroundColor.setTitles(bgColorTitles);
304         mBackgroundColor.setValues(bgColorValues);
305 
306         mBackgroundOpacity = (ColorPreference) mCustom.findPreference(PREF_BACKGROUND_OPACITY);
307         mBackgroundOpacity.setTitles(opacityTitles);
308         mBackgroundOpacity.setValues(opacityValues);
309 
310         mWindowColor = (ColorPreference) mCustom.findPreference(PREF_WINDOW_COLOR);
311         mWindowColor.setTitles(bgColorTitles);
312         mWindowColor.setValues(bgColorValues);
313 
314         mWindowOpacity = (ColorPreference) mCustom.findPreference(PREF_WINDOW_OPACITY);
315         mWindowOpacity.setTitles(opacityTitles);
316         mWindowOpacity.setValues(opacityValues);
317 
318         mEdgeType = (EdgeTypePreference) mCustom.findPreference(PREF_EDGE_TYPE);
319         mTypeface = (ListPreference) mCustom.findPreference(PREF_TYPEFACE);
320     }
321 
installUpdateListeners()322     private void installUpdateListeners() {
323         mPreset.setOnValueChangedListener(this);
324         mForegroundColor.setOnValueChangedListener(this);
325         mForegroundOpacity.setOnValueChangedListener(this);
326         mEdgeColor.setOnValueChangedListener(this);
327         mBackgroundColor.setOnValueChangedListener(this);
328         mBackgroundOpacity.setOnValueChangedListener(this);
329         mWindowColor.setOnValueChangedListener(this);
330         mWindowOpacity.setOnValueChangedListener(this);
331         mEdgeType.setOnValueChangedListener(this);
332 
333         mTypeface.setOnPreferenceChangeListener(this);
334         mFontSize.setOnPreferenceChangeListener(this);
335         mLocale.setOnPreferenceChangeListener(this);
336     }
337 
updateAllPreferences()338     private void updateAllPreferences() {
339         final int preset = mCaptioningManager.getRawUserStyle();
340         mPreset.setValue(preset);
341 
342         final float fontSize = mCaptioningManager.getFontScale();
343         mFontSize.setValue(Float.toString(fontSize));
344 
345         final ContentResolver cr = getContentResolver();
346         final CaptionStyle attrs = CaptionStyle.getCustomStyle(cr);
347         mEdgeType.setValue(attrs.edgeType);
348         mEdgeColor.setValue(attrs.edgeColor);
349 
350         final int foregroundColor = attrs.hasForegroundColor() ?
351                 attrs.foregroundColor : CaptionStyle.COLOR_UNSPECIFIED;
352         parseColorOpacity(mForegroundColor, mForegroundOpacity, foregroundColor);
353 
354         final int backgroundColor = attrs.hasBackgroundColor() ?
355                 attrs.backgroundColor : CaptionStyle.COLOR_UNSPECIFIED;
356         parseColorOpacity(mBackgroundColor, mBackgroundOpacity, backgroundColor);
357 
358         final int windowColor = attrs.hasWindowColor() ?
359                 attrs.windowColor : CaptionStyle.COLOR_UNSPECIFIED;
360         parseColorOpacity(mWindowColor, mWindowOpacity, windowColor);
361 
362         final String rawTypeface = attrs.mRawTypeface;
363         mTypeface.setValue(rawTypeface == null ? "" : rawTypeface);
364 
365         final String rawLocale = mCaptioningManager.getRawLocale();
366         mLocale.setValue(rawLocale == null ? "" : rawLocale);
367     }
368 
369     /**
370      * Unpack the specified color value and update the preferences.
371      *
372      * @param color color preference
373      * @param opacity opacity preference
374      * @param value packed value
375      */
parseColorOpacity(ColorPreference color, ColorPreference opacity, int value)376     private void parseColorOpacity(ColorPreference color, ColorPreference opacity, int value) {
377         final int colorValue;
378         final int opacityValue;
379         if (!CaptionStyle.hasColor(value)) {
380             // "Default" color with variable alpha.
381             colorValue = CaptionStyle.COLOR_UNSPECIFIED;
382             opacityValue = (value & 0xFF) << 24;
383         } else if ((value >>> 24) == 0) {
384             // "None" color with variable alpha.
385             colorValue = Color.TRANSPARENT;
386             opacityValue = (value & 0xFF) << 24;
387         } else {
388             // Normal color.
389             colorValue = value | 0xFF000000;
390             opacityValue = value & 0xFF000000;
391         }
392 
393         // Opacity value is always white.
394         opacity.setValue(opacityValue | 0xFFFFFF);
395         color.setValue(colorValue);
396     }
397 
mergeColorOpacity(ColorPreference color, ColorPreference opacity)398     private int mergeColorOpacity(ColorPreference color, ColorPreference opacity) {
399         final int colorValue = color.getValue();
400         final int opacityValue = opacity.getValue();
401         final int value;
402         // "Default" is 0x00FFFFFF or, for legacy support, 0x00000100.
403         if (!CaptionStyle.hasColor(colorValue)) {
404             // Encode "default" as 0x00FFFFaa.
405             value = 0x00FFFF00 | Color.alpha(opacityValue);
406         } else if (colorValue == Color.TRANSPARENT) {
407             // Encode "none" as 0x000000aa.
408             value = Color.alpha(opacityValue);
409         } else {
410             // Encode custom color normally.
411             value = colorValue & 0x00FFFFFF | opacityValue & 0xFF000000;
412         }
413         return value;
414     }
415 
refreshShowingCustom()416     private void refreshShowingCustom() {
417         final boolean customPreset = mPreset.getValue() == CaptionStyle.PRESET_CUSTOM;
418         if (!customPreset && mShowingCustom) {
419             getPreferenceScreen().removePreference(mCustom);
420             mShowingCustom = false;
421         } else if (customPreset && !mShowingCustom) {
422             getPreferenceScreen().addPreference(mCustom);
423             mShowingCustom = true;
424         }
425     }
426 
427     @Override
onValueChanged(ListDialogPreference preference, int value)428     public void onValueChanged(ListDialogPreference preference, int value) {
429         final ContentResolver cr = getActivity().getContentResolver();
430         if (mForegroundColor == preference || mForegroundOpacity == preference) {
431             final int merged = mergeColorOpacity(mForegroundColor, mForegroundOpacity);
432             Settings.Secure.putInt(
433                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR, merged);
434         } else if (mBackgroundColor == preference || mBackgroundOpacity == preference) {
435             final int merged = mergeColorOpacity(mBackgroundColor, mBackgroundOpacity);
436             Settings.Secure.putInt(
437                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, merged);
438         } else if (mWindowColor == preference || mWindowOpacity == preference) {
439             final int merged = mergeColorOpacity(mWindowColor, mWindowOpacity);
440             Settings.Secure.putInt(
441                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, merged);
442         } else if (mEdgeColor == preference) {
443             Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR, value);
444         } else if (mPreset == preference) {
445             Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, value);
446             refreshShowingCustom();
447         } else if (mEdgeType == preference) {
448             Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE, value);
449         }
450 
451         refreshPreviewText();
452     }
453 
454     @Override
onPreferenceChange(Preference preference, Object value)455     public boolean onPreferenceChange(Preference preference, Object value) {
456         final ContentResolver cr = getActivity().getContentResolver();
457         if (mTypeface == preference) {
458             Settings.Secure.putString(
459                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE, (String) value);
460         } else if (mFontSize == preference) {
461             Settings.Secure.putFloat(
462                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
463                     Float.parseFloat((String) value));
464         } else if (mLocale == preference) {
465             Settings.Secure.putString(
466                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE, (String) value);
467         }
468 
469         refreshPreviewText();
470         return true;
471     }
472 }
473