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