diff --git a/library/res/color/holo_blue_dark_btn.xml b/library/res/color/holo_blue_dark_btn.xml new file mode 100644 index 0000000..53676ae --- /dev/null +++ b/library/res/color/holo_blue_dark_btn.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/library/res/drawable/holo_blue_dark_btn_bg.xml b/library/res/drawable/holo_blue_dark_btn_bg.xml new file mode 100644 index 0000000..8b04d1a --- /dev/null +++ b/library/res/drawable/holo_blue_dark_btn_bg.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/res/drawable/holo_blue_dark_btn_bg_focused.xml b/library/res/drawable/holo_blue_dark_btn_bg_focused.xml new file mode 100644 index 0000000..0fbc5a1 --- /dev/null +++ b/library/res/drawable/holo_blue_dark_btn_bg_focused.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/library/res/drawable/holo_blue_dark_btn_bg_pressed.xml b/library/res/drawable/holo_blue_dark_btn_bg_pressed.xml new file mode 100644 index 0000000..54937d3 --- /dev/null +++ b/library/res/drawable/holo_blue_dark_btn_bg_pressed.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/library/res/values/attrs.xml b/library/res/values/attrs.xml new file mode 100644 index 0000000..a1a4b57 --- /dev/null +++ b/library/res/values/attrs.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/res/values/colors.xml b/library/res/values/colors.xml new file mode 100644 index 0000000..cffd6a0 --- /dev/null +++ b/library/res/values/colors.xml @@ -0,0 +1,48 @@ + + + + + #ff33b5e5 + + #ff99cc00 + + #ffff4444 + + #ff0099cc + + #ff669900 + + #ffcc0000 + + #ffaa66cc + + #ffffbb33 + + #ffff8800 + + #ff00ddff + #fddd + #faaa + + @color/holo_blue_light + @android:color/white + @color/holo_gray_light + @color/holo_gray_dark + \ No newline at end of file diff --git a/library/res/values/strings.xml b/library/res/values/strings.xml new file mode 100644 index 0000000..cdb5f1b --- /dev/null +++ b/library/res/values/strings.xml @@ -0,0 +1,812 @@ + + + \u1F50E + \u2709 + \u2665 + \u2661 + \u2605 + \u1F464 + \u1F3AC + \u1F304 + \u1F4F7 + \u2713 + \u2714 + \u2715 + \u2716 + \u2B + \u2795 + \u2D + \u2796 + \u2753 + \u2139 + \u2302 + \u1F517 + \u1F4CE + \u1F512 + \uE708 + \u1F513 + \uE709 + \u1F4CC + \uE70A + \uE70C + \uE70E + \u1F4E5 + \u1F4E4 + \uE710 + \uE711 + \u275D + \u275E + \u275B + \u275C + \u270E + \u270F + \u2710 + \u21B6 + \uE718 + \uE719 + \uE71A + \uE71B + \uE71C + \uE71D + \uE720 + \uE721 + \uE724 + \uE725 + \uE726 + \uE728 + \uE729 + \uE72A + \uE730 + \uE731 + \uE732 + \uE733 + \uE734 + \uE735 + \u1F4D6 + \u1F4C1 + \u1F4C2 + \u1F4E6 + \uE73A + \uE73B + \u2699 + \u1F527 + \uE73C + \u1F4C5 + \uE73E + \u1F4C6 + \u1F3A4 + \u1F507 + \u1F50A + \u1F3A7 + \u1F554 + \u1F4A1 + \u1F6AB + \uE744 + \uE745 + \uE746 + \uE747 + \u2B0C + \u2B0D + \uE74A + \uE74C + \u2193 + \u2190 + \u2192 + \u2191 + \uE4A4 + \uE4A1 + \uE4A2 + \uE4A3 + \u27F3 + \u1F504 + \u1F501 + \u21C4 + \u2387 + \u2935 + \u25B6 + \uE048 + \u25AA + \u2389 + \u23EE + \u23ED + \u23CF + \u1F3AF + \u1F4F6 + \u1F3C9 + \uE764 + \uE765 + \uE766 + \uE772 + \uE773 + \uE774 + \uE775 + \u1F4F1 + \u1F4BF + \uE795 + \uE796 + \uE797 + \uE798 + \uE799 + \u1F4D5 + \u1F4D4 + \uE79A + \uE79B + \uE79C + \uE79D + \uE79E + \u263C + \u2600 + \u2601 + \u26C6 + \u26A1 + \u263E + \uE7A0 + \u2602 + \u1F4CA + \uE7A2 + \uE7A3 + \u26BF + \u1F511 + \u23 + \u40 + \uB6 + \uE7A4 + + \uF000 + \uF001 + \uF002 + \uF003 + \uF004 + \uF005 + \uF006 + \uF007 + \uF008 + \uF009 + \uF00A + \uF00B + \uF00C + \uF00D + \uF00E + \uF010 + \uF011 + \uF012 + \uF013 + \uF014 + \uF015 + \uF016 + \uF017 + \uF018 + \uF019 + \uF01A + \uF01B + \uF01C + \uF01D + \uF01E + \uF021 + \uF022 + \uF023 + \uF024 + \uF025 + \uF026 + \uF027 + \uF028 + \uF029 + \uF02A + \uF02B + \uF02C + \uF02D + \uF02E + \uF02F + \uF030 + \uF031 + \uF032 + \uF033 + \uF034 + \uF035 + \uF036 + \uF037 + \uF038 + \uF039 + \uF03A + \uF03B + \uF03C + \uF03D + \uF03E + \uF040 + \uF041 + \uF042 + \uF043 + \uF044 + \uF045 + \uF046 + \uF047 + \uF048 + \uF049 + \uF04A + \uF04B + \uF04C + \uF04D + \uF04E + \uF050 + \uF051 + \uF052 + \uF053 + \uF054 + \uF055 + \uF056 + \uF057 + \uF058 + \uF059 + \uF05A + \uF05B + \uF05C + \uF05D + \uF05E + \uF060 + \uF061 + \uF062 + \uF063 + \uF064 + \uF065 + \uF066 + \uF067 + \uF068 + \uF069 + \uF06A + \uF06B + \uF06C + \uF06D + \uF06E + \uF070 + \uF071 + \uF072 + \uF073 + \uF074 + \uF075 + \uF076 + \uF077 + \uF078 + \uF079 + \uF07A + \uF07B + \uF07C + \uF07D + \uF07E + \uF080 + \uF081 + \uF082 + \uF083 + \uF084 + \uF085 + \uF086 + \uF087 + \uF088 + \uF089 + \uF08A + \uF08B + \uF08C + \uF08D + \uF08E + \uF090 + \uF091 + \uF092 + \uF093 + \uF094 + \uF095 + \uF096 + \uF097 + \uF098 + \uF099 + \uF09A + \uF09B + \uF09C + \uF09D + \uF09E + \uF0A0 + \uF0A1 + \uF0F3 + \uF0A3 + \uF0A4 + \uF0A5 + \uF0A6 + \uF0A7 + \uF0A8 + \uF0A9 + \uF0AA + \uF0AB + \uF0AC + \uF0AD + \uF0AE + \uF0B0 + \uF0B1 + \uF0B2 + \uF0C0 + \uF0C1 + \uF0C2 + \uF0C3 + \uF0C4 + \uF0C5 + \uF0C6 + \uF0C7 + \uF0C8 + \uF0C9 + \uF0CA + \uF0CB + \uF0CC + \uF0CD + \uF0CE + \uF0D0 + \uF0D1 + \uF0D2 + \uF0D3 + \uF0D4 + \uF0D5 + \uF0D6 + \uF0D7 + \uF0D8 + \uF0D9 + \uF0DA + \uF0DB + \uF0DC + \uF0DD + \uF0DE + \uF0E0 + \uF0E1 + \uF0E2 + \uF0E3 + \uF0E4 + \uF0E5 + \uF0E6 + \uF0E7 + \uF0E8 + \uF0E9 + \uF0EA + \uF0EB + \uF0EC + \uF0ED + \uF0EE + \uF0F0 + \uF0F1 + \uF0F2 + \uF0A2 + \uF0F4 + \uF0F5 + \uF0F6 + \uF0F7 + \uF0F8 + \uF0F9 + \uF0FA + \uF0FB + \uF0FC + \uF0FD + \uF0FE + \uF100 + \uF101 + \uF102 + \uF103 + \uF104 + \uF105 + \uF106 + \uF107 + \uF108 + \uF109 + \uF10A + \uF10B + \uF10C + \uF10D + \uF10E + \uF110 + \uF111 + \uF112 + \uF113 + \uF114 + \uF115 + \uF118 + \uF119 + \uF11A + \uF11B + \uF11C + \uF11D + \uF11E + \uF120 + \uF121 + \uF122 + \uF122 + \uF123 + \uF124 + \uF125 + \uF126 + \uF127 + \uF128 + \uF129 + \uF12A + \uF12B + \uF12C + \uF12D + \uF12E + \uF130 + \uF131 + \uF132 + \uF133 + \uF134 + \uF135 + \uF136 + \uF137 + \uF138 + \uF139 + \uF13A + \uF13B + \uF13C + \uF13D + \uF13E + \uF140 + \uF141 + \uF142 + \uF143 + \uF144 + \uF145 + \uF146 + \uF147 + \uF148 + \uF149 + \uF14A + \uF14B + \uF14C + \uF14D + \uF14E + \uF150 + \uF151 + \uF152 + \uF153 + \uF154 + \uF155 + \uF156 + \uF157 + \uF158 + \uF159 + \uF15A + \uF15B + \uF15C + \uF15D + \uF15E + \uF160 + \uF161 + \uF162 + \uF163 + \uF164 + \uF165 + \uF166 + \uF167 + \uF168 + \uF169 + \uF16A + \uF16B + \uF16C + \uF16D + \uF16E + \uF170 + \uF171 + \uF172 + \uF173 + \uF174 + \uF175 + \uF176 + \uF177 + \uF178 + \uF179 + \uF17A + \uF17B + \uF17C + \uF17D + \uF17E + \uF180 + \uF181 + \uF182 + \uF183 + \uF184 + \uF185 + \uF186 + \uF187 + \uF188 + \uF189 + \uF18A + \uF18B + \uF18C + \uF18D + \uF18E + \uF190 + \uF191 + \uF192 + \uF193 + \uF194 + \uF195 + \uF196 + + \u1F4DE + \u1F4F1 + \uE789 + \uE723 + \u2709 + \u1F53F + \u270E + \u2712 + \u1F4CE + \uE777 + \uE712 + \uE713 + \u27A6 + \u1F464 + \u1F465 + \uE700 + \uE722 + \uE715 + \uE724 + \uE727 + \uE728 + \u27A2 + \u1F3AF + \uE73C + \uE73E + \u2665 + \u2661 + \u2605 + \u2606 + \u1F44D + \u1F44E + \uE720 + \uE718 + \u275E + \u2302 + \uE74C + \u1F50D + \u1F526 + \uE716 + \u1F514 + \u1F517 + \u2691 + \u2699 + \u2692 + \u1F3C6 + \uE70C + \u1F4F7 + \u1F4E3 + \u263D + \u1F3A8 + \u1F342 + \u266A + \u266B + \u1F4A5 + \u1F393 + \u1F4D5 + \u1F4F0 + \u1F45C + \u2708 + \uE788 + \uE70A + \u1F554 + \u1F3A4 + \u1F4C5 + \u26A1 + \u26C8 + \u1F4A7 + \u1F4BF + \u1F4BC + \u1F4A8 + \u23F3 + \u1F6C7 + \u1F394 + \uE776 + \u1F511 + \u1F50B + \u1F4FE + \uE7A1 + \u1F4FD + \u2615 + \u1F680 + \uE79A + \u1F6C6 + \u1F6C8 + \u1F30E + \u2328 + \uE74E + \uE74D + \uE76B + \uE76A + \uE769 + \uE768 + \u1F505 + \u1F506 + \u25D1 + \uE714 + \u1F4BB + \u221E + \u1F4A1 + \u1F4B3 + \u1F4F8 + \u2707 + \u1F4CB + \uE73D + \u1F4E6 + \u1F3AB + \uE73A + \u1F4F6 + \u1F4FF + \u1F4A6 + \uF601 + \u1F4C8 + \u25F4 + \u1F4CA + \u1F53E + \u1F512 + \u1F513 + \uE741 + \uE740 + \u2713 + \u274C + \u229F + \u229E + \u274E + \u2296 + \u2295 + \u2716 + \u2796 + \u2795 + \u232B + \u1F6AB + \u2139 + \uE705 + \u2753 + \uE704 + \u26A0 + \u1F504 + \u27F3 + \u27F2 + \u1F500 + \u1F519 + \u21B3 + \uE717 + \u1F501 + \uE771 + \u21B0 + \u21C6 + \uE005 + \uE003 + \u268F + \u2630 + \u1F4C4 + \uE731 + \uE730 + \uE736 + \uE737 + \u1F304 + \u1F3AC + \u1F3B5 + \u1F4C1 + \uE800 + \uE729 + \u1F4E4 + \u1F4E5 + \u1F4BE + \uE778 + \u2601 + \uE711 + \u1F516 + \u1F4D1 + \u1F4D6 + \u25B6 + \u2016 + \u25CF + \u25A0 + \u23E9 + \u23EA + \u23EE + \u23ED + \uE744 + \uE746 + \u23F7 + \u1F50A + \u1F507 + \u1F568 + \u1F569 + \u1F56A + \u1F56B + \u1F56C + \uE4AD + \uE4B0 + \uE4AF + \uE4AE + \u2B05 + \u2B07 + \u2B06 + \u27A1 + \uE759 + \uE758 + \uE75B + \uE75A + \u25C2 + \u25BE + \u25B4 + \u25B8 + \uE75D + \uE75C + \uE75F + \uE75E + \uE761 + \uE760 + \uE763 + \uE762 + \uE765 + \uE764 + \uE767 + \uE766 + \u2190 + \u2193 + \u2191 + \u2192 + \uE74F + \u23F6 + \u23F5 + \u23F4 + \u1F545 + \u1F546 + \u1F547 + \u1F548 + \u1F549 + \u1F54A + \u1F54B + \u1F54C + \u1F54D + \u1F54E + \u1F54F + \u1F5F9 + \u1F5FA + + \uF300 + \uF301 + \uF303 + \uF304 + \uF306 + \uF307 + \uF309 + \uF30A + \uF30C + \uF30D + \uF30E + \uF30F + \uF310 + \uF312 + \uF313 + \uF315 + \uF316 + \uF318 + \uF319 + \uF31B + \uF31C + \uF31E + \uF31F + \uF321 + \uF322 + \uF324 + \uF325 + \uF327 + \uF328 + \uF32A + \uF32D + \uF330 + \uF333 + \uF336 + \uF339 + \uF33A + \uF33C + \uF33F + \uF342 + \uF345 + \uF348 + \uF34B + \uF34E + \uF351 + \uF354 + \uF357 + + \ No newline at end of file diff --git a/library/res/values/styles.xml b/library/res/values/styles.xml new file mode 100644 index 0000000..ae3eb28 --- /dev/null +++ b/library/res/values/styles.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/library/src/com/atermenji/android/iconicdroid/IconicFontButton.java b/library/src/com/atermenji/android/iconicdroid/IconicFontButton.java new file mode 100644 index 0000000..4478c2e --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/IconicFontButton.java @@ -0,0 +1,123 @@ + +package com.atermenji.android.iconicdroid; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.widget.Button; + +import com.atermenji.android.iconicdroid.icon.GenericIcon; +import com.atermenji.android.iconicdroid.util.ColorStateList; +import com.atermenji.android.iconicdroid.util.TypefaceManager.IconicTypeface; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class IconicFontButton extends Button { + private static final String TAG = "IconicFontButton"; + private IconicFontLayerDrawable mBackgroundDrawable; + + public static enum FontIdx { + SYS (0, null), // TODO: implement. + ICONIC (1, IconicTypeface.ICONIC), + AWESOME(2, IconicTypeface.FONT_AWESOME), + ENTYPO (3, IconicTypeface.ENTYPO), + SOCIAL (4, IconicTypeface.ENTYPO_SOCIAL); + + private final int mVal; + private final IconicTypeface mTypeface; + private FontIdx(int val, IconicTypeface typeface) { + mVal = val; + mTypeface = typeface; + } + public static FontIdx of(int val) { + return mValMap.get(val); + } + + public int getVal() { + return mVal; + } + + public IconicTypeface getTypeface() { + return mTypeface; + } + + private static final SparseArray mValMap; + static { + mValMap = new SparseArray(); + for (FontIdx font : values()) { + if (BuildConfig.DEBUG && mValMap.get(font.mVal) != null) { + throw new AssertionError("duplicate index detected at " + font.mVal + + " with font " + font); + } + mValMap.put(font.mVal, font); + } + } + } + + public IconicFontButton(Context context) { + this(context, null); + } + + public IconicFontButton(Context context, AttributeSet attrs) { + this(context, attrs, R.style.IconicFontButton); + } + + public IconicFontButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs, defStyle); + } + + @SuppressWarnings("deprecation") + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void setBackground() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + setBackground(mBackgroundDrawable); + } else { + setBackgroundDrawable(mBackgroundDrawable); + } + } + + // TODO: optimizations... + private void init(Context context, AttributeSet attrs, int defStyle) { + Resources r = getResources(); + ColorStateList csl = null; + try { + csl = ColorStateList.createFromXml(r, + r.getXml(R.color.holo_blue_dark_btn)); + } catch (XmlPullParserException e) { + Log.e(TAG, "IconicFontButton.init(): failed parsing ColorStateList", e); + } catch (IOException e) { + Log.e(TAG, "IconicFontButton.init(): failed reading ColorStateList", e); + } + + mBackgroundDrawable = new IconicFontLayerDrawable(new Drawable [] { r.getDrawable(R.drawable.holo_blue_dark_btn_bg) }, + new IconicFontStateListDrawable(getContext(), csl)); + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.IconicFontButton, + defStyle, + 0); + + int idx = 0; + float iconPadding = 0; + String icon = null; + try { + idx = a.getInt(R.styleable.IconicFontButton_font, FontIdx.SYS.getVal()); + iconPadding = a.getDimension(R.styleable.IconicFontButton_iconPadding, 0); + icon = a.getString(R.styleable.IconicFontButton_iconUtf); + } finally { + a.recycle(); + } + + mBackgroundDrawable.setIcon(new GenericIcon(FontIdx.of(idx).getTypeface(), icon)); + mBackgroundDrawable.setIconPadding((int) iconPadding); + setBackground(); + } +} diff --git a/library/src/com/atermenji/android/iconicdroid/IconicFontLayerDrawable.java b/library/src/com/atermenji/android/iconicdroid/IconicFontLayerDrawable.java new file mode 100644 index 0000000..94c82f2 --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/IconicFontLayerDrawable.java @@ -0,0 +1,23 @@ +package com.atermenji.android.iconicdroid; + +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; + +import com.atermenji.android.iconicdroid.icon.Icon; +import com.atermenji.android.iconicdroid.util.ArrayUtils; + +public class IconicFontLayerDrawable extends LayerDrawable { + private IconicFontStateListDrawable mIconDrawable; + public IconicFontLayerDrawable(Drawable[] layers, IconicFontStateListDrawable iconDrawable) { + super(ArrayUtils.appendElement(Drawable.class, layers, iconDrawable)); + mIconDrawable = iconDrawable; + } + + public void setIcon(Icon icon) { + mIconDrawable.setIcon(icon); + } + + public void setIconPadding(int iconPadding) { + mIconDrawable.setIconPadding(iconPadding); + } +} \ No newline at end of file diff --git a/library/src/com/atermenji/android/iconicdroid/IconicFontStateListDrawable.java b/library/src/com/atermenji/android/iconicdroid/IconicFontStateListDrawable.java new file mode 100644 index 0000000..e847446 --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/IconicFontStateListDrawable.java @@ -0,0 +1,41 @@ +package com.atermenji.android.iconicdroid; + +import android.content.Context; +import android.graphics.drawable.StateListDrawable; + +import com.atermenji.android.iconicdroid.icon.Icon; +import com.atermenji.android.iconicdroid.util.ColorStateList; + +public class IconicFontStateListDrawable extends StateListDrawable { + private final IconicFontDrawable mIconDrawable; + private final ColorStateList mColorFilter; + + public IconicFontStateListDrawable(Context context, + ColorStateList colorFilter) { + mIconDrawable = new IconicFontDrawable(context); + /*mIconDrawable.setIconColor(Color.WHITE); // So SRC_IN works. */ + mColorFilter = colorFilter; + for (int[] states : mColorFilter.getStates()) { + addState(states, mIconDrawable); + } + } + + public void setIconPadding(int iconPadding) { + mIconDrawable.setIconPadding(iconPadding); + } + + public void setIcon(Icon icon) { + mIconDrawable.setIcon(icon); + } + + @Override protected boolean onStateChange(int[] state) { + super.onStateChange(state); + if (mColorFilter != null) { + int argb = mColorFilter.getColorForState(state, mColorFilter.getDefaultColor()); + mIconDrawable.setIconColor(argb/*, PorterDuff.Mode.SRC_IN*/); + invalidateSelf(); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/library/src/com/atermenji/android/iconicdroid/icon/GenericIcon.java b/library/src/com/atermenji/android/iconicdroid/icon/GenericIcon.java new file mode 100644 index 0000000..bde23d1 --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/icon/GenericIcon.java @@ -0,0 +1,28 @@ +package com.atermenji.android.iconicdroid.icon; + +import com.atermenji.android.iconicdroid.util.TypefaceManager.IconicTypeface; + +/** Generic class for translating client Strings to internal icon + * representations. */ +public class GenericIcon implements Icon { + private static final long serialVersionUID = 74214504474849939L; + + private final IconicTypeface mTypeface; + private final int mIconUtfValue; + public GenericIcon(IconicTypeface typeface, String iconStr) { + mTypeface = typeface; + mIconUtfValue = getIconUtfValue(iconStr); + } + + @Override public IconicTypeface getIconicTypeface() { + return mTypeface; + } + + @Override public int getIconUtfValue() { + return mIconUtfValue; + } + + public static int getIconUtfValue(String iconStr) { + return Character.codePointAt(iconStr, 0); + } +} diff --git a/library/src/com/atermenji/android/iconicdroid/util/ArrayUtils.java b/library/src/com/atermenji/android/iconicdroid/util/ArrayUtils.java new file mode 100644 index 0000000..dcc6997 --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/util/ArrayUtils.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atermenji.android.iconicdroid.util; + +import java.lang.reflect.Array; + +// XXX these should be changed to reflect the actual memory allocator we use. +// it looks like right now objects want to be powers of 2 minus 8 +// and the array size eats another 4 bytes + +/** + * ArrayUtils contains some methods that you can call to find out + * the most efficient increments by which to grow arrays. + */ +public class ArrayUtils +{ + private static Object[] EMPTY = new Object[0]; + private static final int CACHE_SIZE = 73; + private static Object[] sCache = new Object[CACHE_SIZE]; + + private ArrayUtils() { /* cannot be instantiated */ } + + public static int idealByteArraySize(int need) { + for (int i = 4; i < 32; i++) + if (need <= (1 << i) - 12) + return (1 << i) - 12; + + return need; + } + + public static int idealBooleanArraySize(int need) { + return idealByteArraySize(need); + } + + public static int idealShortArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + public static int idealCharArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + public static int idealIntArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + public static int idealFloatArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + public static int idealObjectArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + public static int idealLongArraySize(int need) { + return idealByteArraySize(need * 8) / 8; + } + + /** + * Checks if the beginnings of two byte arrays are equal. + * + * @param array1 the first byte array + * @param array2 the second byte array + * @param length the number of bytes to check + * @return true if they're equal, false otherwise + */ + public static boolean equals(byte[] array1, byte[] array2, int length) { + if (length < 0) { + throw new IllegalArgumentException(); + } + + if (array1 == array2) { + return true; + } + if (array1 == null || array2 == null || array1.length < length || array2.length < length) { + return false; + } + for (int i = 0; i < length; i++) { + if (array1[i] != array2[i]) { + return false; + } + } + return true; + } + + /** + * Returns an empty array of the specified type. The intent is that + * it will return the same empty array every time to avoid reallocation, + * although this is not guaranteed. + */ + public static T[] emptyArray(Class kind) { + if (kind == Object.class) { + return (T[]) EMPTY; + } + + int bucket = ((System.identityHashCode(kind) / 8) & 0x7FFFFFFF) % CACHE_SIZE; + Object cache = sCache[bucket]; + + if (cache == null || cache.getClass().getComponentType() != kind) { + cache = Array.newInstance(kind, 0); + sCache[bucket] = cache; + + // Log.e("cache", "new empty " + kind.getName() + " at " + bucket); + } + + return (T[]) cache; + } + + /** + * Checks that value is present as at least one of the elements of the array. + * @param array the array to check in + * @param value the value to check for + * @return true if the value is present in the array + */ + public static boolean contains(T[] array, T value) { + return indexOf(array, value) != -1; + } + + /** + * Return first index of {@code value} in {@code array}, or {@code -1} if + * not found. + */ + public static int indexOf(T[] array, T value) { + for (int i = 0; i < array.length; i++) { + if (array[i] == null) { + if (value == null) return i; + } else { + if (value != null && array[i].equals(value)) return i; + } + } + return -1; + } + + /** + * Test if all {@code check} items are contained in {@code array}. + */ + public static boolean containsAll(T[] array, T[] check) { + for (T checkItem : check) { + if (!contains(array, checkItem)) { + return false; + } + } + return true; + } + + public static boolean contains(int[] array, int value) { + for (int element : array) { + if (element == value) { + return true; + } + } + return false; + } + + public static long total(long[] array) { + long total = 0; + for (long value : array) { + total += value; + } + return total; + } + + /** + * Appends an element to a copy of the array and returns the copy. + * @param array The original array, or null to represent an empty array. + * @param element The element to add. + * @return A new array that contains all of the elements of the original array + * with the specified element added at the end. + */ + @SuppressWarnings("unchecked") + public static T[] appendElement(Class kind, T[] array, T element) { + final T[] result; + final int end; + if (array != null) { + end = array.length; + result = (T[])Array.newInstance(kind, end + 1); + System.arraycopy(array, 0, result, 0, end); + } else { + end = 0; + result = (T[])Array.newInstance(kind, 1); + } + result[end] = element; + return result; + } + + /** + * Removes an element from a copy of the array and returns the copy. + * If the element is not present, then the original array is returned unmodified. + * @param array The original array, or null to represent an empty array. + * @param element The element to remove. + * @return A new array that contains all of the elements of the original array + * except the first copy of the specified element removed. If the specified element + * was not present, then returns the original array. Returns null if the result + * would be an empty array. + */ + @SuppressWarnings("unchecked") + public static T[] removeElement(Class kind, T[] array, T element) { + if (array != null) { + final int length = array.length; + for (int i = 0; i < length; i++) { + if (array[i] == element) { + if (length == 1) { + return null; + } + T[] result = (T[])Array.newInstance(kind, length - 1); + System.arraycopy(array, 0, result, 0, i); + System.arraycopy(array, i + 1, result, i, length - i - 1); + return result; + } + } + } + return array; + } + + public static int[] appendInt(int[] cur, int val) { + if (cur == null) { + return new int[] { val }; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + return cur; + } + } + int[] ret = new int[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + + public static int[] removeInt(int[] cur, int val) { + if (cur == null) { + return null; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + int[] ret = new int[N - 1]; + if (i > 0) { + System.arraycopy(cur, 0, ret, 0, i); + } + if (i < (N - 1)) { + System.arraycopy(cur, i + 1, ret, i, N - i - 1); + } + return ret; + } + } + return cur; + } +} diff --git a/library/src/com/atermenji/android/iconicdroid/util/ColorStateList.java b/library/src/com/atermenji/android/iconicdroid/util/ColorStateList.java new file mode 100644 index 0000000..a3c9632 --- /dev/null +++ b/library/src/com/atermenji/android/iconicdroid/util/ColorStateList.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atermenji.android.iconicdroid.util; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.res.Resources; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.StateSet; +import android.util.Xml; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Arrays; + +/** + * + * Lets you map {@link android.view.View} state sets to colors. + * + * {@link android.content.res.ColorStateList}s are created from XML resource files defined in the + * "color" subdirectory directory of an application's resource directory. The XML file contains + * a single "selector" element with a number of "item" elements inside. For example: + * + *
+ * <selector xmlns:android="http://schemas.android.com/apk/res/android">
+ *   <item android:state_focused="true" android:color="@color/testcolor1"/>
+ *   <item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" />
+ *   <item android:state_enabled="false" android:color="@color/testcolor3" />
+ *   <item android:color="@color/testcolor5"/>
+ * </selector>
+ * 
+ * + * This defines a set of state spec / color pairs where each state spec specifies a set of + * states that a view must either be in or not be in and the color specifies the color associated + * with that spec. The list of state specs will be processed in order of the items in the XML file. + * An item with no state spec is considered to match any set of states and is generally useful as + * a final item to be used as a default. Note that if you have such an item before any other items + * in the list then any subsequent items will end up being ignored. + *

For more information, see the guide to Color State + * List Resource.

+ */ +public class ColorStateList implements Parcelable { + + private int[][] mStateSpecs; // must be parallel to mColors + private int[] mColors; // must be parallel to mStateSpecs + private int mDefaultColor = 0xffff0000; + + private static final int[][] EMPTY = new int[][] { new int[0] }; + private static final SparseArray> sCache = + new SparseArray>(); + + private ColorStateList() { } + + /** + * Creates a ColorStateList that returns the specified mapping from + * states to colors. + */ + public ColorStateList(int[][] states, int[] colors) { + mStateSpecs = states; + mColors = colors; + + if (states.length > 0) { + mDefaultColor = colors[0]; + + for (int i = 0; i < states.length; i++) { + if (states[i].length == 0) { + mDefaultColor = colors[i]; + } + } + } + } + + /** + * Creates or retrieves a ColorStateList that always returns a single color. + */ + public static ColorStateList valueOf(int color) { + // TODO: should we collect these eventually? + synchronized (sCache) { + WeakReference ref = sCache.get(color); + ColorStateList csl = ref != null ? ref.get() : null; + + if (csl != null) { + return csl; + } + + csl = new ColorStateList(EMPTY, new int[] { color }); + sCache.put(color, new WeakReference(csl)); + return csl; + } + } + + /** + * Create a ColorStateList from an XML document, given a set of {@link Resources}. + */ + public static ColorStateList createFromXml(Resources r, XmlPullParser parser) + throws XmlPullParserException, IOException { + + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + return createFromXmlInner(r, parser, attrs); + } + + /* Create from inside an XML document. Called on a parser positioned at + * a tag in an XML document, tries to create a ColorStateList from that tag. + * Returns null if the tag is not a valid ColorStateList. + */ + private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser, + AttributeSet attrs) throws XmlPullParserException, IOException { + + ColorStateList colorStateList; + + final String name = parser.getName(); + + if (name.equals("selector")) { + colorStateList = new ColorStateList(); + } else { + throw new XmlPullParserException( + parser.getPositionDescription() + ": invalid drawable tag " + name); + } + + colorStateList.inflate(r, parser, attrs); + return colorStateList; + } + + /** + * Creates a new ColorStateList that has the same states and + * colors as this one but where each color has the specified alpha value + * (0-255). + */ + public ColorStateList withAlpha(int alpha) { + int[] colors = new int[mColors.length]; + + int len = colors.length; + for (int i = 0; i < len; i++) { + colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24); + } + + return new ColorStateList(mStateSpecs, colors); + } + + /** + * Fill in this object based on the contents of an XML "selector" element. + */ + private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + + int type; + + final int innerDepth = parser.getDepth()+1; + int depth; + + int listAllocated = 20; + int listSize = 0; + int[] colorList = new int[listAllocated]; + int[][] stateSpecList = new int[listAllocated][]; + + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth=parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + if (depth > innerDepth || !parser.getName().equals("item")) { + continue; + } + + int colorRes = 0; + int color = 0xffff0000; + boolean haveColor = false; + + int i; + int j = 0; + final int numAttrs = attrs.getAttributeCount(); + int[] stateSpec = new int[numAttrs]; + for (i = 0; i < numAttrs; i++) { + final int stateResId = attrs.getAttributeNameResource(i); + if (stateResId == 0) break; + if (stateResId == android.R.attr.color) { + colorRes = attrs.getAttributeResourceValue(i, 0); + + if (colorRes == 0) { + color = attrs.getAttributeIntValue(i, color); + haveColor = true; + } + } else { + stateSpec[j++] = attrs.getAttributeBooleanValue(i, false) + ? stateResId + : -stateResId; + } + } + stateSpec = StateSet.trimStateSet(stateSpec, j); + + if (colorRes != 0) { + color = r.getColor(colorRes); + } else if (!haveColor) { + throw new XmlPullParserException( + parser.getPositionDescription() + + ": tag requires a 'android:color' attribute."); + } + + if (listSize == 0 || stateSpec.length == 0) { + mDefaultColor = color; + } + + if (listSize + 1 >= listAllocated) { + listAllocated = ArrayUtils.idealIntArraySize(listSize + 1); + + int[] ncolor = new int[listAllocated]; + System.arraycopy(colorList, 0, ncolor, 0, listSize); + + int[][] nstate = new int[listAllocated][]; + System.arraycopy(stateSpecList, 0, nstate, 0, listSize); + + colorList = ncolor; + stateSpecList = nstate; + } + + colorList[listSize] = color; + stateSpecList[listSize] = stateSpec; + listSize++; + } + + mColors = new int[listSize]; + mStateSpecs = new int[listSize][]; + System.arraycopy(colorList, 0, mColors, 0, listSize); + System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize); + } + + public boolean isStateful() { + return mStateSpecs.length > 1; + } + + public int[][] getStates() { + return mStateSpecs; + } + + public int[] getColors() { + return mColors; + } + + /** + * Return the color associated with the given set of {@link android.view.View} states. + * + * @param stateSet an array of {@link android.view.View} states + * @param defaultColor the color to return if there's not state spec in this + * {@link ColorStateList} that matches the stateSet. + * + * @return the color associated with that set of states in this {@link ColorStateList}. + */ + public int getColorForState(int[] stateSet, int defaultColor) { + final int setLength = mStateSpecs.length; + for (int i = 0; i < setLength; i++) { + int[] stateSpec = mStateSpecs[i]; + if (StateSet.stateSetMatches(stateSpec, stateSet)) { + return mColors[i]; + } + } + return defaultColor; + } + + /** + * Return the default color in this {@link ColorStateList}. + * + * @return the default color in this {@link ColorStateList}. + */ + public int getDefaultColor() { + return mDefaultColor; + } + + public String toString() { + return "ColorStateList{" + + "mStateSpecs=" + Arrays.deepToString(mStateSpecs) + + "mColors=" + Arrays.toString(mColors) + + "mDefaultColor=" + mDefaultColor + '}'; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + final int N = mStateSpecs.length; + dest.writeInt(N); + for (int i=0; i CREATOR = + new Parcelable.Creator() { + public ColorStateList[] newArray(int size) { + return new ColorStateList[size]; + } + + public ColorStateList createFromParcel(Parcel source) { + final int N = source.readInt(); + int[][] stateSpecs = new int[N][]; + for (int i=0; i