1/*
2 * Copyright (C) 2007 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.fontlab;
18
19import java.util.Map;
20
21import android.app.Activity;
22import android.graphics.Color;
23import android.graphics.Paint;
24import android.graphics.Typeface;
25import android.graphics.drawable.PaintDrawable;
26import android.os.Bundle;
27import android.view.KeyEvent;
28import android.view.Menu;
29import android.view.MenuItem;
30import android.view.View;
31import android.widget.TextView;
32
33public class FontLab extends Activity {
34    private static final int MIN_SIZE = 1;
35    private static final int MAX_SIZE = 60;
36
37    private static final float SCALE_X_RANGE = 20;
38    private static final int MAX_SCALE_X = 20;
39    private static final int MIN_SCALE_X = -19;   // -20 would make zero-scale
40
41    private static final int MAX_GAMMA = 40;
42    private static final int MIN_GAMMA = 1;
43    private static final float GAMMA_RANGE = 20;
44
45    private static final String[] sText = {
46        "Applications Contacts Maps Google Browser Text messages Address book"
47        + " Development Earth Quake Settings Voicemail Zoolander. Four score"
48        + " and seven years ago our fathers brought forth on this continent, a"
49        + " new nation, conceived in Liberty, and dedicated to the proposition"
50        + " that all men are created equal. Now we are engaged in a great civil"
51        + " war, testing whether that nation, or any nation so conceived and so"
52        + " dedicated, can long endure. We are met on a great battle-field of"
53        + " that war. We have come to dedicate a portion of that field, as a"
54        + " final resting place for those who here gave their lives that that"
55        + " nation might live. It is altogether fitting and proper that we"
56        + " should do this. But, in a larger sense, we can not dedicate - we"
57        + " can not consecrate - we can not hallow - this ground. The brave"
58        + " men, living and dead, who struggled here, have consecrated it, far"
59        + " above our poor power to add or detract. The world will little note,"
60        + " nor long remember what we say here, but it can never forget what"
61        + " they did here. It is for us the living, rather, to be dedicated"
62        + " here to the unfinished work which they who fought here have thus"
63        + " far so nobly advanced. It is rather for us to be here dedicated to"
64        + " the great task remaining before us - that from these honored dead"
65        + " we take increased devotion to that cause for which they gave the"
66        + " last full measure of devotion - that we here highly resolve that"
67        + " these dead shall not have died in vain - that this nation, under"
68        + " God, shall have a new birth of freedom - and that government of the"
69        + " people, by the people, for the people, shall not perish from the"
70        + " earth."
71        ,
72        "A Spanish doctor on Tuesday stood by his opinion that Fidel Castro is recovering from stomach surgery despite a newspaper report stating the Cuban leader is in a serious condition after a number of failed operations."
73        + " When Senator Wayne Allard, Republican of Colorado, announced Monday that he would not seek re-election, the uphill battle for his party to reclaim the Senate in 2008 became an even steeper climb."
74        + " Naomi Campbell was today sentenced to five days' community service and ordered to attend an anger management course after she admitted throwing a mobile phone at her maid."
75        ,
76        "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 !@#$%^&*()-_=+[]\\{}|;':\",./<>?"
77        ,
78        "HaH HbH HcH HdH HeH HfH HgH HhH HiH HjH HkH HlH HmH HnH HoH HpH HqH HrH HsH HtH HuH HvH HwH HxH HyH HzH"
79        + "HAH HBH HCH HDH HEH HFH HGH HHH HIH HJH HKH HLH HMH HNH HOH HPH HQH HRH HSH HTH HUH HVH HWH HXH HYH HZH"
80    };
81
82    private void updateText() {
83        mTextIndex %= sText.length;
84        String s = sText[mTextIndex];
85        mColumn1.setText(s);
86        mColumn2.setText(s);
87    }
88
89    public FontLab() {}
90
91    public void onCreate(Bundle icicle) {
92        super.onCreate(icicle);
93        setContentView(R.layout.font_lab);
94
95        mColumn1 = (TextView)findViewById(R.id.column1);
96        mColumn2 = (TextView)findViewById(R.id.column2);
97        mContentView = findViewById(R.id.content);
98
99
100        mColumn1.setTextSize(mFontSize);
101        mColumn2.setTextSize(mFontSize);
102
103        mColumn1.setTextColor(Color.BLACK);
104        mColumn1.setBackgroundDrawable(new PaintDrawable(Color.WHITE));
105        mColumn2.setTextColor(Color.WHITE);
106        mColumn2.setBackgroundDrawable(new PaintDrawable(Color.BLACK));
107
108        refreshFont();
109        updateTitle();
110        updateText();
111
112        setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
113
114        mColumn1.setPadding(5, 0, 5, 0);
115        mColumn2.setPadding(5, 0, 5, 0);
116    }
117
118    private void updateTitle() {
119        Typeface tf = mColumn1.getTypeface();
120        String title = " PS=" + mFontSize + " X="
121                    + (1 + mTextScaleXDelta/SCALE_X_RANGE)
122                    + " G=" + (mGamma/GAMMA_RANGE)
123                    + " S=" + ((mColumn1.getPaintFlags() & Paint.SUBPIXEL_TEXT_FLAG) != 0 ? 1 : 0)
124                    + " " + sTypefaceName[mFontIndex]
125                    + " " + sStyleName[tf.getStyle()]
126                    ;
127        setTitle(title);
128    }
129
130    /** Called when it is time to initialize the activity state. */
131    protected void onRestoreInstanceState(Bundle state) {
132        super.onRestoreInstanceState(state);
133    }
134
135    protected void onResume() {
136        super.onResume();
137    }
138
139    private static final String sStyleName[] = {
140        "Regular", "Bold", "Italic", "Bold Italic"
141    };
142    private static final String sTypefaceName[] = {
143        "Sans",
144        "Serif",
145        "Mono"
146    };
147    private static final Typeface sTypeface[] = {
148        Typeface.SANS_SERIF,
149        Typeface.SERIF,
150        Typeface.MONOSPACE
151    };
152    private static final int FONT_INDEX_SANS = 0;   // index into sTypeface
153    private static final int FONT_INDEX_SERIF = 1;  // index into sTypeface
154    private static final int FONT_INDEX_MONO = 2;   // index into sTypeface
155
156    private static boolean canSupportStyle(Typeface tf, int styleBits) {
157        tf = Typeface.create(tf, styleBits);
158        return (tf.getStyle() & styleBits) == styleBits;
159    }
160
161    private void refreshFont() {
162        Typeface tf = Typeface.create(sTypeface[mFontIndex], mFontStyle);
163        mColumn1.setTypeface(tf);
164        mColumn2.setTypeface(tf);
165        updateTitle();
166    }
167
168    private MenuItem.OnMenuItemClickListener mFontClickListener = new MenuItem.OnMenuItemClickListener() {
169        public boolean onMenuItemClick(MenuItem item) {
170            mFontIndex = item.getItemId();
171            refreshFont();
172            return true;
173        }
174    };
175
176    private void addFontMenu(Menu menu, int index) {
177        MenuItem item = menu.add(0, index, 0, sTypefaceName[index]);
178        item.setCheckable(true);
179        item.setOnMenuItemClickListener(mFontClickListener);
180        item.setChecked(index == mFontIndex);
181    }
182
183    private MenuItem.OnMenuItemClickListener mStyleClickListener = new MenuItem.OnMenuItemClickListener() {
184        public boolean onMenuItemClick(MenuItem item) {
185            mFontStyle = mFontStyle ^ item.getItemId();
186            refreshFont();
187            return true;
188        }
189    };
190
191    private void addStyleMenu(Menu menu, int style, char shortCut) {
192        MenuItem item = menu.add(0, style, 0, (style == Typeface.BOLD) ? "Bold" : "Italic");
193        item.setCheckable(true);
194        item.setOnMenuItemClickListener(mStyleClickListener);
195        item.setChecked((mFontStyle & style) != 0);
196
197        item.setVisible(canSupportStyle(sTypeface[mFontIndex], style));
198        if (shortCut != 0) {
199            item.setAlphabeticShortcut(shortCut);
200        }
201    }
202
203    private MenuItem.OnMenuItemClickListener mFlagClickListener = new MenuItem.OnMenuItemClickListener() {
204        public boolean onMenuItemClick(MenuItem item) {
205            int mask = item.getItemId();
206            mColumn1.setPaintFlags(mColumn1.getPaintFlags() ^ mask);
207            mColumn2.setPaintFlags(mColumn2.getPaintFlags() ^ mask);
208            updateTitle();
209            return true;
210        }
211    };
212
213    private
214    void addFlagMenu(Menu menu, int paintFlag, String label, char shortCut) {
215        MenuItem item = menu.add(0, paintFlag, 0, label);
216        item.setCheckable(true);
217        item.setOnMenuItemClickListener(mFlagClickListener);
218        item.setChecked((mColumn1.getPaintFlags() & paintFlag) != 0);
219        if (shortCut != 0) {
220            item.setAlphabeticShortcut(shortCut);
221        }
222    }
223
224    private static void addListenerMenu(MenuItem item,
225                                        MenuItem.OnMenuItemClickListener listener,
226                                        char keyChar) {
227        item.setOnMenuItemClickListener(listener);
228        if (keyChar != '\0') {
229            item.setAlphabeticShortcut(keyChar);
230        }
231    }
232
233    public boolean onCreateOptionsMenu(Menu menu) {
234        super.onCreateOptionsMenu(menu);
235        return true;
236    }
237
238    @Override public boolean onPrepareOptionsMenu(Menu menu) {
239        super.onPrepareOptionsMenu(menu);
240        menu.clear();
241
242        addFontMenu(menu, FONT_INDEX_SANS);
243        addFontMenu(menu, FONT_INDEX_SERIF);
244//        addFontMenu(menu, FONT_INDEX_MONO);
245        addStyleMenu(menu, Typeface.BOLD, 'b');
246        addStyleMenu(menu, Typeface.ITALIC, 'i');
247        addFlagMenu(menu, Paint.SUBPIXEL_TEXT_FLAG, "SubPixel", 's');
248        //        addFlagMenu(menu, Paint.DEV_KERN_TEXT_FLAG, "DevKern", 'k');
249        menu.add(0, 0, 0, "Text").setOnMenuItemClickListener(mTextCallback).setAlphabeticShortcut('t');
250
251        return true;
252    }
253
254    protected void onActivityResult(int requestCode, int resultCode,
255                                    String data, Map extras) {
256        if (resultCode == RESULT_OK) {
257            switch (requestCode) {
258            case BACKGROUND_PICKED:
259                {
260                    int color = ((Integer)extras.get("text")).intValue();
261                    mColumn1.setTextColor(color);
262                    mColumn2.setTextColor(color);
263
264                    int colorTranslucent = (color & 0x00FFFFFF) + 0x77000000;
265
266                    setTitleColor(color);
267
268                    Integer texture = (Integer)extras.get("texture");
269                    if (texture != null) {
270                        mContentView.setBackgroundResource(texture.intValue());
271                    } else {
272                        color = ((Integer)extras.get("bgcolor")).intValue();
273                        mContentView.setBackgroundColor(color);
274                    }
275                }
276                break;
277            }
278        }
279    }
280
281    @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
282        int size = mFontSize;
283        int scaleX = mTextScaleXDelta;
284
285        switch (keyCode) {
286            case KeyEvent.KEYCODE_DPAD_DOWN:
287                size -= 1;
288                break;
289            case KeyEvent.KEYCODE_DPAD_UP:
290                size += 1;
291                break;
292            case KeyEvent.KEYCODE_DPAD_RIGHT:
293                scaleX += 1;
294                break;
295            case KeyEvent.KEYCODE_DPAD_LEFT:
296                scaleX -= 1;
297                break;
298            case KeyEvent.KEYCODE_U:
299            case KeyEvent.KEYCODE_VOLUME_UP:
300                changeGamma(1);
301                return true;
302            case KeyEvent.KEYCODE_D:
303            case KeyEvent.KEYCODE_VOLUME_DOWN:
304                changeGamma(-1);
305                return true;
306            default:
307                return super.onKeyDown(keyCode, event);
308        }
309
310        size = Math.min(MAX_SIZE, Math.max(MIN_SIZE, size));
311        if (size != mFontSize) {
312            mFontSize = size;
313            mColumn1.setTextSize(mFontSize);
314            mColumn2.setTextSize(mFontSize);
315            updateTitle();
316            return true;
317        }
318
319        scaleX = Math.min(MAX_SCALE_X, Math.max(MIN_SCALE_X, scaleX));
320        if (scaleX != mTextScaleXDelta) {
321            mTextScaleXDelta = scaleX;
322            mColumn1.setTextScaleX(1 + scaleX / SCALE_X_RANGE);
323            mColumn2.setTextScaleX(1 + scaleX / SCALE_X_RANGE);
324            updateTitle();
325            return true;
326        }
327
328        return super.onKeyDown(keyCode, event);
329    }
330
331    // default to gamma of 1.0
332    private int mGamma = Math.round(1.0f * GAMMA_RANGE);
333
334    private void changeGamma(int delta) {
335        int gamma = Math.min(MAX_GAMMA, Math.max(MIN_GAMMA, mGamma + delta));
336        if (gamma != mGamma) {
337            mGamma = gamma;
338            updateTitle();
339            float blackGamma = mGamma / GAMMA_RANGE;
340            Typeface.setGammaForText(blackGamma, 1 / blackGamma);
341            mContentView.invalidate();
342        }
343    }
344
345    private void setFont(TextView t, TextView f, Map extras) {
346        int style = ((Integer)extras.get("style")).intValue();
347        String font = (String)extras.get("font");
348        t.setTypeface(Typeface.create(font, style));
349
350        f.setText((String)extras.get("title"));
351    }
352
353    MenuItem.OnMenuItemClickListener mTextCallback = new MenuItem.OnMenuItemClickListener() {
354        public boolean onMenuItemClick(MenuItem item) {
355            mTextIndex += 1;
356            updateText();
357            return true;
358        }
359    };
360
361    private static final int BACKGROUND_PICKED = 1;
362
363    private TextView mColumn1;
364    private TextView mColumn2;
365    private View mContentView;
366    private int mFontIndex = FONT_INDEX_SANS;
367    private int mFontStyle = Typeface.NORMAL;
368    private int mFontSize = 18;
369    private int mTextIndex;
370    private int mTextScaleXDelta;
371}
372
373