- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / infobar / TranslateLanguagePanel.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.infobar;
6
7 import android.content.Context;
8 import android.graphics.Color;
9 import android.text.SpannableString;
10 import android.text.TextUtils;
11 import android.text.style.ForegroundColorSpan;
12 import android.view.Gravity;
13 import android.view.LayoutInflater;
14 import android.view.View;
15 import android.view.View.OnClickListener;
16 import android.view.ViewGroup;
17 import android.widget.AdapterView;
18 import android.widget.ArrayAdapter;
19 import android.widget.Button;
20 import android.widget.FrameLayout;
21 import android.widget.LinearLayout;
22 import android.widget.Spinner;
23 import android.widget.TextView;
24
25
26 import org.chromium.chrome.browser.infobar.InfoBar;
27 import org.chromium.chrome.browser.infobar.InfoBarLayout;
28 import org.chromium.chrome.R;
29
30 import java.util.ArrayList;
31 import java.util.List;
32
33 /**
34  * Language panel shown in the translate infobar.
35  */
36 public class TranslateLanguagePanel
37         implements TranslateSubPanel, AdapterView.OnItemSelectedListener {
38
39     private static final int LANGUAGE_TYPE_SOURCE = 0;
40     private static final int LANGUAGE_TYPE_TARGET = 1;
41
42     // UI elements.
43     private Spinner mSourceSpinner;
44     private Spinner mTargetSpinner;
45
46     // Items that are not interacted with.
47     // Provided by the caller, the new languages will be set here if the user
48     // clicks "done".
49     private final TranslateOptions mOptions;
50
51     // This object will be used to keep the state for the time the
52     // panel is opened it can be totally discarded in the end if the user
53     // clicks "cancel".
54     private TranslateOptions mSessionOptions;
55
56     private LanguageArrayAdapter mSourceAdapter;
57     private LanguageArrayAdapter mTargetAdapter;
58
59     private final SubPanelListener mListener;
60
61     /**
62      * Display language drop downs so they can be picked as source or
63      * target for a translation.
64      *
65      * @param listener triggered when the panel is closed
66      * @param options will be modified with the new languages selected.
67      */
68     public TranslateLanguagePanel(SubPanelListener listener, TranslateOptions options) {
69         mListener = listener;
70         mOptions = options;
71         mSessionOptions = new TranslateOptions(mOptions);
72     }
73
74     @Override
75     public void createContent(Context context, InfoBarLayout layout) {
76         mSourceSpinner = null;
77         mTargetSpinner = null;
78
79         String changeLanguage = context.getString(R.string.translate_infobar_change_languages);
80         TextView panelMessage = (TextView) layout.findViewById(R.id.infobar_message);
81         panelMessage.setText(changeLanguage);
82
83         // Set up the spinners.
84         createSpinners(context);
85         layout.addGroup(mSourceSpinner, mTargetSpinner);
86
87         // Set up the buttons.
88         layout.addButtons(context.getString(R.string.translate_button_done),
89                 context.getString(R.string.cancel));
90     }
91
92     @Override
93     public void onButtonClicked(boolean primary) {
94         if (primary) {
95             mOptions.setSourceLanguage(mSessionOptions.sourceLanguageIndex());
96             mOptions.setTargetLanguage(mSessionOptions.targetLanguageIndex());
97         }
98         mListener.onPanelClosed(InfoBar.ACTION_TYPE_NONE);
99     }
100
101     private void createSpinners(Context context) {
102         mSourceAdapter = new LanguageArrayAdapter(context, R.layout.translate_spinner,
103                 LANGUAGE_TYPE_SOURCE);
104         mTargetAdapter = new LanguageArrayAdapter(context, R.layout.translate_spinner,
105                 LANGUAGE_TYPE_TARGET);
106
107         // Determine how wide each spinner needs to be to avoid truncating its children.
108         mSourceAdapter.addAll(createSpinnerLanguages(-1));
109         mTargetAdapter.addAll(createSpinnerLanguages(-1));
110         mSourceAdapter.measureWidthRequiredForView();
111         mTargetAdapter.measureWidthRequiredForView();
112
113         // Create the spinners.
114         mSourceSpinner = new Spinner(context);
115         mTargetSpinner = new Spinner(context);
116         mSourceSpinner.setOnItemSelectedListener(this);
117         mTargetSpinner.setOnItemSelectedListener(this);
118         mSourceSpinner.setAdapter(mSourceAdapter);
119         mTargetSpinner.setAdapter(mTargetAdapter);
120         reloadSpinners();
121     }
122
123     private void reloadSpinners() {
124         mSourceAdapter.clear();
125         mTargetAdapter.clear();
126
127         int sourceAvoidLanguage = mSessionOptions.targetLanguageIndex();
128         int targetAvoidLanguage = mSessionOptions.sourceLanguageIndex();
129         mSourceAdapter.addAll(createSpinnerLanguages(sourceAvoidLanguage));
130         mTargetAdapter.addAll(createSpinnerLanguages(targetAvoidLanguage));
131
132         int originalSourceSelection = mSourceSpinner.getSelectedItemPosition();
133         int newSourceSelection = getSelectionPosition(LANGUAGE_TYPE_SOURCE);
134         if (originalSourceSelection != newSourceSelection)
135             mSourceSpinner.setSelection(newSourceSelection);
136
137         int originalTargetSelection = mTargetSpinner.getSelectedItemPosition();
138         int newTargetSelection = getSelectionPosition(LANGUAGE_TYPE_TARGET);
139         if (originalTargetSelection != newTargetSelection)
140             mTargetSpinner.setSelection(newTargetSelection);
141     }
142
143     private int getSelectionPosition(int languageType) {
144         int position = languageType == LANGUAGE_TYPE_SOURCE ? mSessionOptions.sourceLanguageIndex()
145                 : mSessionOptions.targetLanguageIndex();
146
147         // Since the source and target languages cannot appear in both spinners, the index for the
148         // source language can be off by one if comes after the target language alphabetically (and
149         // vice versa).
150         int opposite = languageType == LANGUAGE_TYPE_SOURCE ? mSessionOptions.targetLanguageIndex()
151                 : mSessionOptions.sourceLanguageIndex();
152         if (opposite < position) position -= 1;
153
154         return position;
155     }
156
157     @Override
158     public void onItemSelected(AdapterView<?> adapter, View view, int position, long id) {
159         Spinner spinner = (Spinner) adapter;
160         int newId = ((SpinnerLanguageElement) spinner.getSelectedItem()).getLanguageId();
161         if (spinner == mSourceSpinner) {
162             mSessionOptions.setSourceLanguage(newId);
163         } else {
164             mSessionOptions.setTargetLanguage(newId);
165         }
166         reloadSpinners();
167     }
168
169     @Override
170     public void onNothingSelected(AdapterView<?> adapter) {
171     }
172
173     /**
174      * Determines what languages will be shown in the Spinner.
175      * @param avoidLanguage Index of the language to avoid.  Use -1 to display all languages.
176      */
177     private ArrayList<SpinnerLanguageElement> createSpinnerLanguages(int avoidLanguage) {
178         ArrayList<SpinnerLanguageElement> result = new ArrayList<SpinnerLanguageElement>();
179         List<String> languages = mSessionOptions.allLanguages();
180         for (int i = 0; i <  languages.size(); ++i) {
181             if (i != avoidLanguage) {
182                 result.add(new SpinnerLanguageElement(languages.get(i), i));
183             }
184         }
185         return result;
186     }
187
188     /**
189      * The drop down view displayed to show the currently selected value.
190      */
191     private static class LanguageArrayAdapter extends ArrayAdapter<SpinnerLanguageElement> {
192         private final SpannableString mTextTemplate;
193         private int mMinimumWidth;
194
195         public LanguageArrayAdapter(Context context, int textViewResourceId,
196                 int languageType) {
197             super(context, textViewResourceId);
198
199             // Get the string that we will display inside the Spinner, indicating whether the
200             // spinner is used for the source or target language.
201             String textTemplate = languageType == LANGUAGE_TYPE_SOURCE
202                     ? context.getString(R.string.translate_options_source_hint)
203                     : context.getString(R.string.translate_options_target_hint);
204             mTextTemplate = new SpannableString(textTemplate);
205             mTextTemplate.setSpan(
206                     new ForegroundColorSpan(Color.GRAY), 0, textTemplate.length(), 0);
207         }
208
209         /** Measures how large the view needs to be to avoid truncating its children. */
210         public void measureWidthRequiredForView() {
211             mMinimumWidth = 0;
212
213             final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
214
215             FrameLayout layout = new FrameLayout(getContext());
216             TextView estimator = (TextView) LayoutInflater.from(getContext()).inflate(
217                     R.layout.infobar_text, null);
218             layout.addView(estimator);
219             for (int i = 0; i < getCount(); ++i) {
220                 estimator.setText(getStringForLanguage(i));
221                 estimator.measure(spec, spec);
222                 mMinimumWidth = Math.max(mMinimumWidth, estimator.getMeasuredWidth());
223             }
224         }
225
226         @Override
227         public View getDropDownView(int position, View convertView, ViewGroup parent) {
228             TextView result;
229             if (!(convertView instanceof TextView)) {
230                 result = (TextView) LayoutInflater.from(getContext()).inflate(
231                         R.layout.infobar_spinner_item, null);
232             } else {
233                 result = (TextView) convertView;
234             }
235
236             String language = ((SpinnerLanguageElement) getItem(position)).toString();
237             result.setText(language);
238             return result;
239         }
240
241         @Override
242         public View getView(int position, View convertView, ViewGroup parent) {
243             TextView result;
244             if (!(convertView instanceof TextView)) {
245                 result = (TextView) LayoutInflater.from(getContext()).inflate(
246                         R.layout.infobar_text, null);
247             } else {
248                 result = (TextView) convertView;
249             }
250             result.setEllipsize(TextUtils.TruncateAt.END);
251             result.setMaxLines(1);
252             result.setText(getStringForLanguage(position));
253             result.setMinWidth(mMinimumWidth);
254             return result;
255         }
256
257         private CharSequence getStringForLanguage(int position) {
258             // The spinners prepend a string to show if they're for the source or target language.
259             String language = getItem(position).toString();
260             SpannableString lang = new SpannableString(language);
261             lang.setSpan(new ForegroundColorSpan(Color.BLACK), 0, lang.length(), 0);
262             return TextUtils.expandTemplate(mTextTemplate, lang);
263         }
264     }
265
266     /**
267      * The element that goes inside the spinner.
268      */
269     private static class SpinnerLanguageElement {
270         private final String mLanguageName;
271         private final int mLanguageId;
272
273         public SpinnerLanguageElement(String languageName, int languageId) {
274             mLanguageName = languageName;
275             mLanguageId = languageId;
276         }
277
278         public int getLanguageId() {
279             return mLanguageId;
280         }
281
282         /**
283          * This is the text displayed in the spinner element so make sure no debug information
284          * is added.
285          */
286         @Override
287         public String toString() {
288             return mLanguageName;
289         }
290     }
291 }