content: package com.norbsoft.typefacehelper;
  
  import android.app.Activity;
  import android.content.Context;
  import android.graphics.Paint;
  import android.graphics.Typeface;
  import android.text.Spannable;
  import android.text.SpannableString;
  import android.text.TextPaint;
  import android.text.style.MetricAffectingSpan;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  
  /**
   * Helper class for setting custom typefaces to {@link android.view.View} and
   * {@link android.view.ViewGroup}. Supports String styling using {@link android.text.SpannableString}
   *
   * Applies typeface to all child views recursively.
   *
   * For detailed information and guide: https://github.com/jskierbi/android-typeface-helper
   *
   * @author Jakub Skierbiszewski
   */
  public class TypefaceHelper {
  
  	private static TypefaceCollection sDefaultTypefaceCollection;
  
  	/**
  	 * Initialize helper with typeface collection.
  	 * This should be called inside {@link android.app.Application#onCreate()}
  	 * @param typefaceCollection
  	 */
  	public static void init(TypefaceCollection typefaceCollection) {
  		sDefaultTypefaceCollection = typefaceCollection;
  	}
  
  	/**
  	 * Return spannable string with default typeface style (style: Typeface.NORMAL)
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param context to obtain string resource
  	 * @param strResId string resource id, content
  	 *
  	 * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(Context context, int strResId) {
  		if (sDefaultTypefaceCollection == null) {
  			throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?");
  		}
  		return typeface(context.getString(strResId), sDefaultTypefaceCollection, Typeface.NORMAL);
  	}
  
  	/**
  	 * Return spannable string with typeface in certain style
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param context to obtain string resource
  	 * @param strResId string resource id, content
  	 * @param style    Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC
  	 *
  	 * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been calledC
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(Context context, int strResId, int style) {
  		if (sDefaultTypefaceCollection == null) {
  			throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?");
  		}
  		return typeface(context.getString(strResId), sDefaultTypefaceCollection, style);
  	}
  
  	/**
  	 * Return spannable string with typeface in certain style
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param context to obtain string resource
  	 * @param strResId string resource id, content
  	 * @param collection TypefaceCollection instance
  	 * @param style    Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC
  	 *
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(Context context, int strResId, TypefaceCollection collection, int style) {
  		return typeface(context.getString(strResId), collection, style);
  	}
  
  	/**
  	 * Return spannable string with default typeface style (style: Typeface.NORMAL)
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param sequence to typeface typeface to
  	 *
  	 * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(CharSequence sequence) {
  		if (sDefaultTypefaceCollection == null) {
  			throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?");
  		}
  		return typeface(sequence, sDefaultTypefaceCollection, Typeface.NORMAL);
  	}
  
  	/**
  	 * Return spannable string with applied typeface in certain style
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param sequence    content
  	 * @param typefaceCollection Collection of typefaces
  	 * @param style     Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC
  	 * @throws IllegalArgumentException when invalid style is passed
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(CharSequence sequence, TypefaceCollection typefaceCollection, int style) {
  		checkTypefaceStyleThrowing(style);
  		SpannableString s = new SpannableString(sequence);
  		s.setSpan(new TypefaceSpan(typefaceCollection.getTypeface(style)), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  		return s;
  	}
  
  	/**
  	 * Return spannable string with default typeface style (style: Typeface.NORMAL)
  	 * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title
  	 *
  	 * @param sequence    content
  	 * @param typefaceCollection Collection of typefaces
  	 * @return SpannableString that can be used in TextView.setText() method
  	 */
  	public static SpannableString typeface(CharSequence sequence, TypefaceCollection typefaceCollection) {
  		return typeface(sequence, typefaceCollection, Typeface.NORMAL);
  	}
  
  	/**
  	 * Apply typefaces to main acitivty view (android.R.id.content).
  	 * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called
  	 * @param activity to typeface custom typefaces to
  	 */
  	public static void typeface(Activity activity) {
  		if (sDefaultTypefaceCollection == null) {
  			throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?");
  		}
  		typeface(activity.findViewById(android.R.id.content), sDefaultTypefaceCollection);
  	}
  
  	/**
  	 * Apply typefaces to view
  	 * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called
  	 * @param view to typeface custom typefaces to
  	 */
  	public static void typeface(View view) {
  		if (sDefaultTypefaceCollection == null) {
  			throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?");
  		}
  		typeface(view, sDefaultTypefaceCollection);
  	}
  
  	/**
  	 * Apply typefaces to main acitivty view (android.R.id.content).
  	 * @param activity to typeface custom typefaces to
  	 * @param typefaceCollection to obtain typefaces SparseArray.
  	 */
  	public static void typeface(Activity activity, TypefaceCollection typefaceCollection) {
  		typeface(activity.findViewById(android.R.id.content), typefaceCollection);
  	}
  
  	/**
  	 * Apply fonts from map to all children of view (or view itself)
  	 *
  	 * @param view view for which to typeface fonts
  	 * @param typefaceCollection Collection of typefaces
  	 */
  	public static void typeface(View view, TypefaceCollection typefaceCollection) {
  
  		if (view instanceof ViewGroup) {
  			applyTypeface((ViewGroup) view, typefaceCollection);
  		} else {
  			applyForView(view, typefaceCollection);
  		}
  	}
  
  	/**
  	 * Check if typeface style int is one of:
  	 * <ul>
  	 *      <li>{@link android.graphics.Typeface#NORMAL}</li>
  	 *      <li>{@link android.graphics.Typeface#BOLD}</li>
  	 *      <li>{@link android.graphics.Typeface#ITALIC}</li>
  	 *      <li>{@link android.graphics.Typeface#BOLD_ITALIC}</li>
  	 * </ul>
  	 * @param style
  	 */
  	private static void checkTypefaceStyleThrowing(int style) {
  		switch (style) {
  		case Typeface.NORMAL:
  		case Typeface.BOLD:
  		case Typeface.ITALIC:
  		case Typeface.BOLD_ITALIC:
  			break;
  		default:
  			throw new IllegalArgumentException("Style have to be in (Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC)");
  		}
  	}
  
  	/**
  	 * Apply typeface to all ViewGroup childs
  	 *
  	 * @param viewGroup to typeface typeface
  	 * @param typefaceCollection typeface collection
  	 */
  	private static void applyTypeface(ViewGroup viewGroup, TypefaceCollection typefaceCollection) {
  
  		for (int i = 0; i < viewGroup.getChildCount(); i++) {
  			View childView = viewGroup.getChildAt(i);
  			if (childView instanceof ViewGroup) {
  				applyTypeface((ViewGroup) childView, typefaceCollection);
  			} else {
  				applyForView(childView, typefaceCollection);
  			}
  		}
  	}
  
  	/**
  	 * Apply typeface to single view
  	 *
  	 * @param view      to typeface typeface
  	 * @param typefaceCollection typeface collection
  	 */
  	private static void applyForView(View view, TypefaceCollection typefaceCollection) {
  
  		if (view instanceof TextView) {
  			TextView textView = (TextView) view;
  			Typeface oldTypeface = textView.getTypeface();
  			final int style = oldTypeface == null ? Typeface.NORMAL : oldTypeface.getStyle();
  			textView.setTypeface(typefaceCollection.getTypeface(style));
  			textView.setPaintFlags(textView.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
  		}
  	}
  
  	private static class TypefaceSpan extends MetricAffectingSpan {
  
  		Typeface typeface;
  
  		TypefaceSpan(Typeface typeface) {
  			this.typeface = typeface;
  		}
  
  		@Override public void updateMeasureState(TextPaint p) {
  			p.setTypeface(typeface);
  			p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
  		}
  
  		@Override public void updateDrawState(TextPaint tp) {
  			tp.setTypeface(typeface);
  			tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
  		}
  	}
  }
  