package org.holoeverywhere; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.graphics.Typeface; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public final class FontLoader { public static interface FontSelector { public HoloFont getFontForView(View view); } public static class HoloFont implements FontSelector { public static final HoloFont ROBOTO; public static final HoloFont ROBOTO_BOLD; public static final HoloFont ROBOTO_BOLD_ITALIC; public static final HoloFont ROBOTO_ITALIC; public static final HoloFont ROBOTO_REGULAR; static { ROBOTO = makeFont( ROBOTO_REGULAR = makeFont(R.raw.roboto_regular), ROBOTO_BOLD = makeFont(R.raw.roboto_bold), ROBOTO_ITALIC = makeFont(R.raw.roboto_italic), ROBOTO_BOLD_ITALIC = makeFont(R.raw.roboto_bolditalic) ); } public static HoloFont makeFont(HoloFont regular, HoloFont bold, HoloFont italic, HoloFont boldItalic) { return new HoloFontMerger(regular, bold, italic, boldItalic); } public static HoloFont makeFont(int rawResourceId) { return new HoloFont(rawResourceId); } public static HoloFont makeFont(int rawResourceId, boolean ignore) { return new HoloFont(rawResourceId, ignore); } public static HoloFont makeFont(Typeface typeface) { return new HoloFont(typeface); } /** * Font raw resource id */ protected int mFontId; /** * If this flag setted this font doesn't modify any view */ protected boolean mIgnore; /** * Loaded typeface */ protected Typeface mTypeface; private HoloFont(int font) { this(font, false); } private HoloFont(int font, boolean ignore) { mFontId = font; mIgnore = ignore; mTypeface = null; } private HoloFont(Typeface typeface) { mFontId = -1; mIgnore = false; mTypeface = typeface; } public <T extends View> T apply(T view) { if (mIgnore || mTypeface == null && mFontId <= 0 || view == null) { return view; } return FontLoader.apply(view, obtainTypeface(view.getContext())); } @Override public HoloFont getFontForView(View view) { return this; } public Typeface obtainTypeface(Context context) { if (mTypeface != null) { return mTypeface; } if (mTypeface == null && mFontId > 0) { mTypeface = loadTypeface(context, mFontId); } return mTypeface; } } private static final class HoloFontMerger extends HoloFont { private HoloFont mBold; private HoloFont mBoldItalic; private HoloFont mItalic; private HoloFont mRegular; public HoloFontMerger(HoloFont regular, HoloFont bold, HoloFont italic, HoloFont boldItalic) { super(regular.mFontId, false); mRegular = regular; mBold = bold; mItalic = italic; mBoldItalic = boldItalic; } @Override public HoloFont getFontForView(View view) { if (!(view instanceof TextView)) { return super.getFontForView(view); } TextView textView = (TextView) view; Typeface typeface = textView.getTypeface(); if (typeface == null) { return mRegular; } final boolean bold = typeface.isBold(), italic = typeface.isItalic(); if (bold && italic) { return mBoldItalic; } else if (bold) { return mBold; } else if (italic) { return mItalic; } else { return mRegular; } } } private static HoloFont sDefaultFont = HoloFont.ROBOTO; private static final SparseArray<Typeface> sFontCache = new SparseArray<Typeface>(); private static final String TAG = FontLoader.class.getSimpleName(); public static <T extends View> T apply(T view) { if (sDefaultFont == null) { return view; } return apply(view, sDefaultFont); } public static <T extends View> T apply(T view, FontSelector fontSelector) { if (view == null || view.getContext() == null || view.getContext().isRestricted()) { Log.e(FontLoader.TAG, "View or context is invalid"); return view; } return internalApply(view, fontSelector); } public static <T extends View> T apply(T view, HoloFont font) { return apply(view, (FontSelector) font); } @SuppressLint("NewApi") public static <T extends View> T apply(T view, int font) { if (view == null || view.getContext() == null || view.getContext().isRestricted()) { Log.e(FontLoader.TAG, "View or context is invalid"); return view; } Typeface typeface = FontLoader.loadTypeface(view.getContext(), font); if (typeface == null) { Log.v(FontLoader.TAG, "Font " + font + " not found in resources"); return view; } else { return FontLoader.apply(view, typeface); } } public static <T extends View> T apply(T view, Typeface typeface) { if (view == null || view.getContext() == null || view.getContext().isRestricted()) { return view; } if (typeface == null) { Log.v(FontLoader.TAG, "Font is null"); return view; } if (view instanceof TextView) { ((TextView) view).setTypeface(typeface); } if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; for (int i = 0; i < group.getChildCount(); i++) { FontLoader.apply(group.getChildAt(i), typeface); } } return view; } public static HoloFont getDefaultFont() { return sDefaultFont; } private static <T extends View> T internalApply(T view, FontSelector fontSelector) { if (view instanceof TextView) { TextView textView = (TextView) view; HoloFont font = fontSelector.getFontForView(textView); if (font != null && !font.mIgnore) { Typeface typeface = font.obtainTypeface(view.getContext()); if (typeface != null) { textView.setTypeface(typeface); } } } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int childCount = group.getChildCount(); for (int i = 0; i < childCount; i++) { internalApply(group.getChildAt(i), fontSelector); } } return view; } public static Typeface loadTypeface(Context context, int font) { Typeface typeface = FontLoader.sFontCache.get(font); if (typeface == null) { try { File file = new File(context.getCacheDir(), "fonts"); if (!file.exists()) { file.mkdirs(); } file = new File(file, "font_0x" + Integer.toHexString(font)); FontLoader.sFontCache.put(font, typeface = readTypeface(file, context.getResources(), font, true)); } catch (Exception e) { Log.e(FontLoader.TAG, "Error of loading font", e); } } return typeface; } private static Typeface readTypeface(File file, Resources res, int font, boolean allowReadExistsFile) throws Exception { try { if (!allowReadExistsFile || !file.exists()) { InputStream is = new BufferedInputStream(res.openRawResource(font)); OutputStream os = new ByteArrayOutputStream(Math.max(is.available(), 1024)); byte[] buffer = new byte[1024]; int read; while ((read = is.read(buffer)) > 0) { os.write(buffer, 0, read); } is.close(); os.flush(); buffer = ((ByteArrayOutputStream) os).toByteArray(); os.close(); os = new FileOutputStream(file); os.write(buffer); os.flush(); os.close(); } return Typeface.createFromFile(file); } catch (Exception e) { if (allowReadExistsFile) { return readTypeface(file, res, font, false); } throw e; } } public static void setDefaultFont(HoloFont defaultFont) { sDefaultFont = defaultFont; } private FontLoader() { } }