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 17 package com.android.fontlab; 18 19 import java.util.Map; 20 21 import android.app.Activity; 22 import android.graphics.Color; 23 import android.graphics.Paint; 24 import android.graphics.Typeface; 25 import android.graphics.drawable.PaintDrawable; 26 import android.os.Bundle; 27 import android.view.KeyEvent; 28 import android.view.Menu; 29 import android.view.MenuItem; 30 import android.view.View; 31 import android.widget.TextView; 32 33 public 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 updateText()82 private void updateText() { 83 mTextIndex %= sText.length; 84 String s = sText[mTextIndex]; 85 mColumn1.setText(s); 86 mColumn2.setText(s); 87 } 88 FontLab()89 public FontLab() {} 90 onCreate(Bundle icicle)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 updateTitle()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. */ onRestoreInstanceState(Bundle state)131 protected void onRestoreInstanceState(Bundle state) { 132 super.onRestoreInstanceState(state); 133 } 134 onResume()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 canSupportStyle(Typeface tf, int styleBits)156 private static boolean canSupportStyle(Typeface tf, int styleBits) { 157 tf = Typeface.create(tf, styleBits); 158 return (tf.getStyle() & styleBits) == styleBits; 159 } 160 refreshFont()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 addFontMenu(Menu menu, int index)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 addStyleMenu(Menu menu, int style, char shortCut)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 addFlagMenu(Menu menu, int paintFlag, String label, char shortCut)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 addListenerMenu(MenuItem item, MenuItem.OnMenuItemClickListener listener, char keyChar)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 onCreateOptionsMenu(Menu menu)233 public boolean onCreateOptionsMenu(Menu menu) { 234 super.onCreateOptionsMenu(menu); 235 return true; 236 } 237 onPrepareOptionsMenu(Menu menu)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 onActivityResult(int requestCode, int resultCode, String data, Map extras)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 onKeyDown(int keyCode, KeyEvent event)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 changeGamma(int delta)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 setFont(TextView t, TextView f, Map extras)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