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