diff --git a/app/build.gradle b/app/build.gradle
index 7aa0dc0f25..bb72725052 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -169,6 +169,7 @@ allprojects {
dependencies {
wearApp project(':wear')
+ implementation project(':iconify')
implementation project(':shared')
implementation project(':core')
implementation project(':automation')
diff --git a/combo/build.gradle b/combo/build.gradle
index a3faa431e6..e20cc0fc66 100644
--- a/combo/build.gradle
+++ b/combo/build.gradle
@@ -15,4 +15,5 @@ android {
dependencies {
implementation project(':core')
implementation project(':shared')
+ implementation project(':iconify')
}
\ No newline at end of file
diff --git a/core/core_dependencies.gradle b/core/core_dependencies.gradle
index 5b4df7cbe5..320e025b5c 100644
--- a/core/core_dependencies.gradle
+++ b/core/core_dependencies.gradle
@@ -69,8 +69,6 @@ dependencies {
api "com.squareup.retrofit2:adapter-rxjava3:$retrofit2_version"
api "com.squareup.retrofit2:converter-gson:$retrofit2_version"
- api "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2"
-
api "androidx.fragment:fragment-ktx:$fragmentktx_version"
api "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
api "androidx.navigation:navigation-runtime-ktx:$nav_version"
diff --git a/dana/build.gradle b/dana/build.gradle
index f03521d269..5a166b3ab2 100644
--- a/dana/build.gradle
+++ b/dana/build.gradle
@@ -25,6 +25,7 @@ android {
dependencies {
implementation project(':core')
implementation project(':shared')
+ implementation project(':iconify')
api "androidx.room:room-ktx:$room_version"
api "androidx.room:room-runtime:$room_version"
diff --git a/diaconn/build.gradle b/diaconn/build.gradle
index 84034c07ae..64ac7fee1f 100644
--- a/diaconn/build.gradle
+++ b/diaconn/build.gradle
@@ -25,6 +25,7 @@ android {
dependencies {
implementation project(':core')
implementation project(':shared')
+ implementation project(':iconify')
api "androidx.room:room-ktx:$room_version"
api "androidx.room:room-runtime:$room_version"
diff --git a/iconify/.gitignore b/iconify/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/iconify/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/iconify/build.gradle b/iconify/build.gradle
new file mode 100644
index 0000000000..3166680705
--- /dev/null
+++ b/iconify/build.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-allopen'
+apply plugin: 'com.hiya.jacoco-android'
+apply plugin: 'kotlinx-serialization'
+
+apply from: "${project.rootDir}/core/android_dependencies.gradle"
+apply from: "${project.rootDir}/core/android_module_dependencies.gradle"
+apply from: "${project.rootDir}/core/test_dependencies.gradle"
+apply from: "${project.rootDir}/core/jacoco_global.gradle"
+
+android {
+
+ namespace 'com.joanzapata.iconify'
+}
+
+dependencies {
+ api "androidx.core:core-ktx:$core_version"
+}
\ No newline at end of file
diff --git a/iconify/consumer-rules.pro b/iconify/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/iconify/proguard-rules.pro b/iconify/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/iconify/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/iconify/src/main/AndroidManifest.xml b/iconify/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..a5918e68ab
--- /dev/null
+++ b/iconify/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+ * new IconDrawable(context, IconValue.icon_star) + * .colorRes(R.color.white) + * .actionBarSize(); + *+ * If you don't set the size of the drawable, it will use the size + * that is given to him. Note that in an ActionBar, if you don't + * set the size explicitly it uses 0, so please use actionBarSize(). + */ +public class IconDrawable extends Drawable { + + public static final int ANDROID_ACTIONBAR_ICON_SIZE_DP = 24; + + private Context context; + + private Icon icon; + + private TextPaint paint; + + private int size = -1; + + private int alpha = 255; + + /** + * Create an IconDrawable. + * @param context Your activity or application context. + * @param iconKey The icon key you want this drawable to display. + * @throws IllegalArgumentException if the key doesn't match any icon. + */ + public IconDrawable(Context context, String iconKey) { + Icon icon = Iconify.findIconForKey(iconKey); + if (icon == null) { + throw new IllegalArgumentException("No icon with that key \"" + iconKey + "\"."); + } + init(context, icon); + } + + /** + * Create an IconDrawable. + * @param context Your activity or application context. + * @param icon The icon you want this drawable to display. + */ + public IconDrawable(Context context, Icon icon) { + init(context, icon); + } + + private void init(Context context, Icon icon) { + this.context = context; + this.icon = icon; + paint = new TextPaint(); + IconFontDescriptorWrapper descriptor = Iconify.findTypefaceOf(icon); + if (descriptor == null) { + throw new IllegalStateException("Unable to find the module associated " + + "with icon " + icon.key() + ", have you registered the module " + + "you are trying to use with Iconify.with(...) in your Application?"); + } + paint.setTypeface(descriptor.getTypeface(context)); + paint.setStyle(Paint.Style.FILL); + paint.setTextAlign(Paint.Align.CENTER); + paint.setUnderlineText(false); + paint.setColor(Color.BLACK); + paint.setAntiAlias(true); + } + + /** + * Set the size of this icon to the standard Android ActionBar. + * @return The current IconDrawable for chaining. + */ + public IconDrawable actionBarSize() { + return sizeDp(ANDROID_ACTIONBAR_ICON_SIZE_DP); + } + + /** + * Set the size of the drawable. + * @param dimenRes The dimension resource. + * @return The current IconDrawable for chaining. + */ + public IconDrawable sizeRes(int dimenRes) { + return sizePx(context.getResources().getDimensionPixelSize(dimenRes)); + } + + /** + * Set the size of the drawable. + * @param size The size in density-independent pixels (dp). + * @return The current IconDrawable for chaining. + */ + public IconDrawable sizeDp(int size) { + return sizePx(convertDpToPx(context, size)); + } + + /** + * Set the size of the drawable. + * @param size The size in pixels (px). + * @return The current IconDrawable for chaining. + */ + public IconDrawable sizePx(int size) { + this.size = size; + setBounds(0, 0, size, size); + invalidateSelf(); + return this; + } + + /** + * Set the color of the drawable. + * @param color The color, usually from android.graphics.Color or 0xFF012345. + * @return The current IconDrawable for chaining. + */ + public IconDrawable color(int color) { + paint.setColor(color); + invalidateSelf(); + return this; + } + + /** + * Set the color of the drawable. + * @param colorRes The color resource, from your R file. + * @return The current IconDrawable for chaining. + */ + public IconDrawable colorRes(int colorRes) { + paint.setColor(context.getColor(colorRes)); + invalidateSelf(); + return this; + } + + /** + * Set the alpha of this drawable. + * @param alpha The alpha, between 0 (transparent) and 255 (opaque). + * @return The current IconDrawable for chaining. + */ + public IconDrawable alpha(int alpha) { + setAlpha(alpha); + invalidateSelf(); + return this; + } + + @Override + public int getIntrinsicHeight() { + return size; + } + + @Override + public int getIntrinsicWidth() { + return size; + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + int height = bounds.height(); + paint.setTextSize(height); + Rect textBounds = new Rect(); + String textValue = String.valueOf(icon.character()); + paint.getTextBounds(textValue, 0, 1, textBounds); + int textHeight = textBounds.height(); + float textBottom = bounds.top + (height - textHeight) / 2f + textHeight - textBounds.bottom; + canvas.drawText(textValue, bounds.exactCenterX(), textBottom, paint); + } + + @Override + public boolean isStateful() { + return true; + } + + @Override + public boolean setState(int[] stateSet) { + int oldValue = paint.getAlpha(); + int newValue = isEnabled(stateSet) ? alpha : alpha / 2; + paint.setAlpha(newValue); + return oldValue != newValue; + } + + @Override + public void setAlpha(int alpha) { + this.alpha = alpha; + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + paint.setColorFilter(cf); + } + + @Override + public void clearColorFilter() { + paint.setColorFilter(null); + } + + @SuppressLint("WrongConstant") @Override + public int getOpacity() { + return this.alpha; + } + + /** + * Sets paint style. + * @param style to be applied + */ + public void setStyle(Paint.Style style) { + paint.setStyle(style); + } + + // Util + private boolean isEnabled(int[] stateSet) { + for (int state : stateSet) + if (state == android.R.attr.state_enabled) + return true; + return false; + } + + // Util + private int convertDpToPx(Context context, float dp) { + return (int) TypedValue.applyDimension( + COMPLEX_UNIT_DIP, dp, + context.getResources().getDisplayMetrics()); + } +} \ No newline at end of file diff --git a/iconify/src/main/java/com/joanzapata/iconify/IconFontDescriptor.java b/iconify/src/main/java/com/joanzapata/iconify/IconFontDescriptor.java new file mode 100644 index 0000000000..beb82eb5ff --- /dev/null +++ b/iconify/src/main/java/com/joanzapata/iconify/IconFontDescriptor.java @@ -0,0 +1,17 @@ +package com.joanzapata.iconify; + +/** + * An IconFontDescriptor defines a TTF font file + * and is able to map keys with characters in this file. + */ +public interface IconFontDescriptor { + + /** + * The TTF file name. + * @return a name with no slash, present in the assets. + */ + String ttfFileName(); + + Icon[] characters(); + +} diff --git a/iconify/src/main/java/com/joanzapata/iconify/Iconify.java b/iconify/src/main/java/com/joanzapata/iconify/Iconify.java new file mode 100644 index 0000000000..fd5f0a8d23 --- /dev/null +++ b/iconify/src/main/java/com/joanzapata/iconify/Iconify.java @@ -0,0 +1,111 @@ +package com.joanzapata.iconify; + +import android.content.Context; +import android.widget.TextView; +import com.joanzapata.iconify.internal.IconFontDescriptorWrapper; +import com.joanzapata.iconify.internal.ParsingUtil; + +import java.util.ArrayList; +import java.util.List; + +public class Iconify { + + /** List of icon font descriptors */ + private static List
+ * This is a one time call. If you call {@link TextView#setText(CharSequence)} after this,
+ * you'll need to call it again.
+ * @param textViews The TextView(s) to enhance.
+ */
+ public static void addIcons(TextView... textViews) {
+ for (TextView textView : textViews) {
+ if (textView == null) continue;
+ textView.setText(compute(textView.getContext(), textView.getText(), textView));
+ }
+ }
+
+ private static void addIconFontDescriptor(IconFontDescriptor iconFontDescriptor) {
+
+ // Prevent duplicates
+ for (IconFontDescriptorWrapper wrapper : iconFontDescriptors) {
+ if (wrapper.getIconFontDescriptor().ttfFileName()
+ .equals(iconFontDescriptor.ttfFileName())) {
+ return;
+ }
+ }
+
+ // Add to the list
+ iconFontDescriptors.add(new IconFontDescriptorWrapper(iconFontDescriptor));
+
+ }
+
+ public static CharSequence compute(Context context, CharSequence text) {
+ return compute(context, text, null);
+ }
+
+ public static CharSequence compute(Context context, CharSequence text, TextView target) {
+ return ParsingUtil.parse(context, iconFontDescriptors, text, target);
+ }
+
+ /**
+ * Allows chain calls on {@link Iconify#with(IconFontDescriptor)}.
+ */
+ public static class IconifyInitializer {
+
+ public IconifyInitializer(IconFontDescriptor iconFontDescriptor) {
+ Iconify.addIconFontDescriptor(iconFontDescriptor);
+ }
+
+ /**
+ * Add support for a new icon font.
+ * @param iconFontDescriptor The IconDescriptor holding the ttf file reference and its mappings.
+ * @return An initializer instance for chain calls.
+ */
+ public IconifyInitializer with(IconFontDescriptor iconFontDescriptor) {
+ Iconify.addIconFontDescriptor(iconFontDescriptor);
+ return this;
+ }
+ }
+
+ /**
+ * Finds the Typeface to apply for a given icon.
+ * @param icon The icon for which you need the typeface.
+ * @return The font descriptor which contains info about the typeface to apply, or null
+ * if the icon cannot be found. In that case, check that you properly added the modules
+ * using {@link #with(IconFontDescriptor)}} prior to calling this method.
+ */
+ public static IconFontDescriptorWrapper findTypefaceOf(Icon icon) {
+ for (IconFontDescriptorWrapper iconFontDescriptor : iconFontDescriptors) {
+ if (iconFontDescriptor.hasIcon(icon)) {
+ return iconFontDescriptor;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Retrieve an icon from a key,
+ * @return The icon, or null if no icon matches the key.
+ */
+ public static Icon findIconForKey(String iconKey) {
+ for (int i = 0, iconFontDescriptorsSize = iconFontDescriptors.size(); i < iconFontDescriptorsSize; i++) {
+ IconFontDescriptorWrapper iconFontDescriptor = iconFontDescriptors.get(i);
+ Icon icon = iconFontDescriptor.getIcon(iconKey);
+ if (icon != null) return icon;
+ }
+ return null;
+ }
+}
diff --git a/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeIcons.java b/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeIcons.java
new file mode 100644
index 0000000000..16202e243c
--- /dev/null
+++ b/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeIcons.java
@@ -0,0 +1,716 @@
+package com.joanzapata.iconify.fonts;
+
+import com.joanzapata.iconify.Icon;
+
+public enum FontAwesomeIcons implements Icon {
+ fa_glass('\uf000'),
+ fa_music('\uf001'),
+ fa_search('\uf002'),
+ fa_envelope_o('\uf003'),
+ fa_heart('\uf004'),
+ fa_star('\uf005'),
+ fa_star_o('\uf006'),
+ fa_user('\uf007'),
+ fa_film('\uf008'),
+ fa_th_large('\uf009'),
+ fa_th('\uf00a'),
+ fa_th_list('\uf00b'),
+ fa_check('\uf00c'),
+ fa_remove('\uf00d'),
+ fa_close('\uf00d'),
+ fa_times('\uf00d'),
+ fa_search_plus('\uf00e'),
+ fa_search_minus('\uf010'),
+ fa_power_off('\uf011'),
+ fa_signal('\uf012'),
+ fa_gear('\uf013'),
+ fa_cog('\uf013'),
+ fa_trash_o('\uf014'),
+ fa_home('\uf015'),
+ fa_file_o('\uf016'),
+ fa_clock_o('\uf017'),
+ fa_road('\uf018'),
+ fa_download('\uf019'),
+ fa_arrow_circle_o_down('\uf01a'),
+ fa_arrow_circle_o_up('\uf01b'),
+ fa_inbox('\uf01c'),
+ fa_play_circle_o('\uf01d'),
+ fa_rotate_right('\uf01e'),
+ fa_repeat('\uf01e'),
+ fa_refresh('\uf021'),
+ fa_list_alt('\uf022'),
+ fa_lock('\uf023'),
+ fa_flag('\uf024'),
+ fa_headphones('\uf025'),
+ fa_volume_off('\uf026'),
+ fa_volume_down('\uf027'),
+ fa_volume_up('\uf028'),
+ fa_qrcode('\uf029'),
+ fa_barcode('\uf02a'),
+ fa_tag('\uf02b'),
+ fa_tags('\uf02c'),
+ fa_book('\uf02d'),
+ fa_bookmark('\uf02e'),
+ fa_print('\uf02f'),
+ fa_camera('\uf030'),
+ fa_font('\uf031'),
+ fa_bold('\uf032'),
+ fa_italic('\uf033'),
+ fa_text_height('\uf034'),
+ fa_text_width('\uf035'),
+ fa_align_left('\uf036'),
+ fa_align_center('\uf037'),
+ fa_align_right('\uf038'),
+ fa_align_justify('\uf039'),
+ fa_list('\uf03a'),
+ fa_dedent('\uf03b'),
+ fa_outdent('\uf03b'),
+ fa_indent('\uf03c'),
+ fa_video_camera('\uf03d'),
+ fa_photo('\uf03e'),
+ fa_image('\uf03e'),
+ fa_picture_o('\uf03e'),
+ fa_pencil('\uf040'),
+ fa_map_marker('\uf041'),
+ fa_adjust('\uf042'),
+ fa_tint('\uf043'),
+ fa_edit('\uf044'),
+ fa_pencil_square_o('\uf044'),
+ fa_share_square_o('\uf045'),
+ fa_check_square_o('\uf046'),
+ fa_arrows('\uf047'),
+ fa_step_backward('\uf048'),
+ fa_fast_backward('\uf049'),
+ fa_backward('\uf04a'),
+ fa_play('\uf04b'),
+ fa_pause('\uf04c'),
+ fa_stop('\uf04d'),
+ fa_forward('\uf04e'),
+ fa_fast_forward('\uf050'),
+ fa_step_forward('\uf051'),
+ fa_eject('\uf052'),
+ fa_chevron_left('\uf053'),
+ fa_chevron_right('\uf054'),
+ fa_plus_circle('\uf055'),
+ fa_minus_circle('\uf056'),
+ fa_times_circle('\uf057'),
+ fa_check_circle('\uf058'),
+ fa_question_circle('\uf059'),
+ fa_info_circle('\uf05a'),
+ fa_crosshairs('\uf05b'),
+ fa_times_circle_o('\uf05c'),
+ fa_check_circle_o('\uf05d'),
+ fa_ban('\uf05e'),
+ fa_arrow_left('\uf060'),
+ fa_arrow_right('\uf061'),
+ fa_arrow_up('\uf062'),
+ fa_arrow_down('\uf063'),
+ fa_mail_forward('\uf064'),
+ fa_share('\uf064'),
+ fa_expand('\uf065'),
+ fa_compress('\uf066'),
+ fa_plus('\uf067'),
+ fa_minus('\uf068'),
+ fa_asterisk('\uf069'),
+ fa_exclamation_circle('\uf06a'),
+ fa_gift('\uf06b'),
+ fa_leaf('\uf06c'),
+ fa_fire('\uf06d'),
+ fa_eye('\uf06e'),
+ fa_eye_slash('\uf070'),
+ fa_warning('\uf071'),
+ fa_exclamation_triangle('\uf071'),
+ fa_plane('\uf072'),
+ fa_calendar('\uf073'),
+ fa_random('\uf074'),
+ fa_comment('\uf075'),
+ fa_magnet('\uf076'),
+ fa_chevron_up('\uf077'),
+ fa_chevron_down('\uf078'),
+ fa_retweet('\uf079'),
+ fa_shopping_cart('\uf07a'),
+ fa_folder('\uf07b'),
+ fa_folder_open('\uf07c'),
+ fa_arrows_v('\uf07d'),
+ fa_arrows_h('\uf07e'),
+ fa_bar_chart_o('\uf080'),
+ fa_bar_chart('\uf080'),
+ fa_twitter_square('\uf081'),
+ fa_facebook_square('\uf082'),
+ fa_camera_retro('\uf083'),
+ fa_key('\uf084'),
+ fa_gears('\uf085'),
+ fa_cogs('\uf085'),
+ fa_comments('\uf086'),
+ fa_thumbs_o_up('\uf087'),
+ fa_thumbs_o_down('\uf088'),
+ fa_star_half('\uf089'),
+ fa_heart_o('\uf08a'),
+ fa_sign_out('\uf08b'),
+ fa_linkedin_square('\uf08c'),
+ fa_thumb_tack('\uf08d'),
+ fa_external_link('\uf08e'),
+ fa_sign_in('\uf090'),
+ fa_trophy('\uf091'),
+ fa_github_square('\uf092'),
+ fa_upload('\uf093'),
+ fa_lemon_o('\uf094'),
+ fa_phone('\uf095'),
+ fa_square_o('\uf096'),
+ fa_bookmark_o('\uf097'),
+ fa_phone_square('\uf098'),
+ fa_twitter('\uf099'),
+ fa_facebook_f('\uf09a'),
+ fa_facebook('\uf09a'),
+ fa_github('\uf09b'),
+ fa_unlock('\uf09c'),
+ fa_credit_card('\uf09d'),
+ fa_feed('\uf09e'),
+ fa_rss('\uf09e'),
+ fa_hdd_o('\uf0a0'),
+ fa_bullhorn('\uf0a1'),
+ fa_bell('\uf0f3'),
+ fa_certificate('\uf0a3'),
+ fa_hand_o_right('\uf0a4'),
+ fa_hand_o_left('\uf0a5'),
+ fa_hand_o_up('\uf0a6'),
+ fa_hand_o_down('\uf0a7'),
+ fa_arrow_circle_left('\uf0a8'),
+ fa_arrow_circle_right('\uf0a9'),
+ fa_arrow_circle_up('\uf0aa'),
+ fa_arrow_circle_down('\uf0ab'),
+ fa_globe('\uf0ac'),
+ fa_wrench('\uf0ad'),
+ fa_tasks('\uf0ae'),
+ fa_filter('\uf0b0'),
+ fa_briefcase('\uf0b1'),
+ fa_arrows_alt('\uf0b2'),
+ fa_group('\uf0c0'),
+ fa_users('\uf0c0'),
+ fa_chain('\uf0c1'),
+ fa_link('\uf0c1'),
+ fa_cloud('\uf0c2'),
+ fa_flask('\uf0c3'),
+ fa_cut('\uf0c4'),
+ fa_scissors('\uf0c4'),
+ fa_copy('\uf0c5'),
+ fa_files_o('\uf0c5'),
+ fa_paperclip('\uf0c6'),
+ fa_save('\uf0c7'),
+ fa_floppy_o('\uf0c7'),
+ fa_square('\uf0c8'),
+ fa_navicon('\uf0c9'),
+ fa_reorder('\uf0c9'),
+ fa_bars('\uf0c9'),
+ fa_list_ul('\uf0ca'),
+ fa_list_ol('\uf0cb'),
+ fa_strikethrough('\uf0cc'),
+ fa_underline('\uf0cd'),
+ fa_table('\uf0ce'),
+ fa_magic('\uf0d0'),
+ fa_truck('\uf0d1'),
+ fa_pinterest('\uf0d2'),
+ fa_pinterest_square('\uf0d3'),
+ fa_google_plus_square('\uf0d4'),
+ fa_google_plus('\uf0d5'),
+ fa_money('\uf0d6'),
+ fa_caret_down('\uf0d7'),
+ fa_caret_up('\uf0d8'),
+ fa_caret_left('\uf0d9'),
+ fa_caret_right('\uf0da'),
+ fa_columns('\uf0db'),
+ fa_unsorted('\uf0dc'),
+ fa_sort('\uf0dc'),
+ fa_sort_down('\uf0dd'),
+ fa_sort_desc('\uf0dd'),
+ fa_sort_up('\uf0de'),
+ fa_sort_asc('\uf0de'),
+ fa_envelope('\uf0e0'),
+ fa_linkedin('\uf0e1'),
+ fa_rotate_left('\uf0e2'),
+ fa_undo('\uf0e2'),
+ fa_legal('\uf0e3'),
+ fa_gavel('\uf0e3'),
+ fa_dashboard('\uf0e4'),
+ fa_tachometer('\uf0e4'),
+ fa_comment_o('\uf0e5'),
+ fa_comments_o('\uf0e6'),
+ fa_flash('\uf0e7'),
+ fa_bolt('\uf0e7'),
+ fa_sitemap('\uf0e8'),
+ fa_umbrella('\uf0e9'),
+ fa_paste('\uf0ea'),
+ fa_clipboard('\uf0ea'),
+ fa_lightbulb_o('\uf0eb'),
+ fa_exchange('\uf0ec'),
+ fa_cloud_download('\uf0ed'),
+ fa_cloud_upload('\uf0ee'),
+ fa_user_md('\uf0f0'),
+ fa_stethoscope('\uf0f1'),
+ fa_suitcase('\uf0f2'),
+ fa_bell_o('\uf0a2'),
+ fa_coffee('\uf0f4'),
+ fa_cutlery('\uf0f5'),
+ fa_file_text_o('\uf0f6'),
+ fa_building_o('\uf0f7'),
+ fa_hospital_o('\uf0f8'),
+ fa_ambulance('\uf0f9'),
+ fa_medkit('\uf0fa'),
+ fa_fighter_jet('\uf0fb'),
+ fa_beer('\uf0fc'),
+ fa_h_square('\uf0fd'),
+ fa_plus_square('\uf0fe'),
+ fa_angle_double_left('\uf100'),
+ fa_angle_double_right('\uf101'),
+ fa_angle_double_up('\uf102'),
+ fa_angle_double_down('\uf103'),
+ fa_angle_left('\uf104'),
+ fa_angle_right('\uf105'),
+ fa_angle_up('\uf106'),
+ fa_angle_down('\uf107'),
+ fa_desktop('\uf108'),
+ fa_laptop('\uf109'),
+ fa_tablet('\uf10a'),
+ fa_mobile_phone('\uf10b'),
+ fa_mobile('\uf10b'),
+ fa_circle_o('\uf10c'),
+ fa_quote_left('\uf10d'),
+ fa_quote_right('\uf10e'),
+ fa_spinner('\uf110'),
+ fa_circle('\uf111'),
+ fa_mail_reply('\uf112'),
+ fa_reply('\uf112'),
+ fa_github_alt('\uf113'),
+ fa_folder_o('\uf114'),
+ fa_folder_open_o('\uf115'),
+ fa_smile_o('\uf118'),
+ fa_frown_o('\uf119'),
+ fa_meh_o('\uf11a'),
+ fa_gamepad('\uf11b'),
+ fa_keyboard_o('\uf11c'),
+ fa_flag_o('\uf11d'),
+ fa_flag_checkered('\uf11e'),
+ fa_terminal('\uf120'),
+ fa_code('\uf121'),
+ fa_mail_reply_all('\uf122'),
+ fa_reply_all('\uf122'),
+ fa_star_half_empty('\uf123'),
+ fa_star_half_full('\uf123'),
+ fa_star_half_o('\uf123'),
+ fa_location_arrow('\uf124'),
+ fa_crop('\uf125'),
+ fa_code_fork('\uf126'),
+ fa_unlink('\uf127'),
+ fa_chain_broken('\uf127'),
+ fa_question('\uf128'),
+ fa_info('\uf129'),
+ fa_exclamation('\uf12a'),
+ fa_superscript('\uf12b'),
+ fa_subscript('\uf12c'),
+ fa_eraser('\uf12d'),
+ fa_puzzle_piece('\uf12e'),
+ fa_microphone('\uf130'),
+ fa_microphone_slash('\uf131'),
+ fa_shield('\uf132'),
+ fa_calendar_o('\uf133'),
+ fa_fire_extinguisher('\uf134'),
+ fa_rocket('\uf135'),
+ fa_maxcdn('\uf136'),
+ fa_chevron_circle_left('\uf137'),
+ fa_chevron_circle_right('\uf138'),
+ fa_chevron_circle_up('\uf139'),
+ fa_chevron_circle_down('\uf13a'),
+ fa_html5('\uf13b'),
+ fa_css3('\uf13c'),
+ fa_anchor('\uf13d'),
+ fa_unlock_alt('\uf13e'),
+ fa_bullseye('\uf140'),
+ fa_ellipsis_h('\uf141'),
+ fa_ellipsis_v('\uf142'),
+ fa_rss_square('\uf143'),
+ fa_play_circle('\uf144'),
+ fa_ticket('\uf145'),
+ fa_minus_square('\uf146'),
+ fa_minus_square_o('\uf147'),
+ fa_level_up('\uf148'),
+ fa_level_down('\uf149'),
+ fa_check_square('\uf14a'),
+ fa_pencil_square('\uf14b'),
+ fa_external_link_square('\uf14c'),
+ fa_share_square('\uf14d'),
+ fa_compass('\uf14e'),
+ fa_toggle_down('\uf150'),
+ fa_caret_square_o_down('\uf150'),
+ fa_toggle_up('\uf151'),
+ fa_caret_square_o_up('\uf151'),
+ fa_toggle_right('\uf152'),
+ fa_caret_square_o_right('\uf152'),
+ fa_euro('\uf153'),
+ fa_eur('\uf153'),
+ fa_gbp('\uf154'),
+ fa_dollar('\uf155'),
+ fa_usd('\uf155'),
+ fa_rupee('\uf156'),
+ fa_inr('\uf156'),
+ fa_cny('\uf157'),
+ fa_rmb('\uf157'),
+ fa_yen('\uf157'),
+ fa_jpy('\uf157'),
+ fa_ruble('\uf158'),
+ fa_rouble('\uf158'),
+ fa_rub('\uf158'),
+ fa_won('\uf159'),
+ fa_krw('\uf159'),
+ fa_bitcoin('\uf15a'),
+ fa_btc('\uf15a'),
+ fa_file('\uf15b'),
+ fa_file_text('\uf15c'),
+ fa_sort_alpha_asc('\uf15d'),
+ fa_sort_alpha_desc('\uf15e'),
+ fa_sort_amount_asc('\uf160'),
+ fa_sort_amount_desc('\uf161'),
+ fa_sort_numeric_asc('\uf162'),
+ fa_sort_numeric_desc('\uf163'),
+ fa_thumbs_up('\uf164'),
+ fa_thumbs_down('\uf165'),
+ fa_youtube_square('\uf166'),
+ fa_youtube('\uf167'),
+ fa_xing('\uf168'),
+ fa_xing_square('\uf169'),
+ fa_youtube_play('\uf16a'),
+ fa_dropbox('\uf16b'),
+ fa_stack_overflow('\uf16c'),
+ fa_instagram('\uf16d'),
+ fa_flickr('\uf16e'),
+ fa_adn('\uf170'),
+ fa_bitbucket('\uf171'),
+ fa_bitbucket_square('\uf172'),
+ fa_tumblr('\uf173'),
+ fa_tumblr_square('\uf174'),
+ fa_long_arrow_down('\uf175'),
+ fa_long_arrow_up('\uf176'),
+ fa_long_arrow_left('\uf177'),
+ fa_long_arrow_right('\uf178'),
+ fa_apple('\uf179'),
+ fa_windows('\uf17a'),
+ fa_android('\uf17b'),
+ fa_linux('\uf17c'),
+ fa_dribbble('\uf17d'),
+ fa_skype('\uf17e'),
+ fa_foursquare('\uf180'),
+ fa_trello('\uf181'),
+ fa_female('\uf182'),
+ fa_male('\uf183'),
+ fa_gittip('\uf184'),
+ fa_gratipay('\uf184'),
+ fa_sun_o('\uf185'),
+ fa_moon_o('\uf186'),
+ fa_archive('\uf187'),
+ fa_bug('\uf188'),
+ fa_vk('\uf189'),
+ fa_weibo('\uf18a'),
+ fa_renren('\uf18b'),
+ fa_pagelines('\uf18c'),
+ fa_stack_exchange('\uf18d'),
+ fa_arrow_circle_o_right('\uf18e'),
+ fa_arrow_circle_o_left('\uf190'),
+ fa_toggle_left('\uf191'),
+ fa_caret_square_o_left('\uf191'),
+ fa_dot_circle_o('\uf192'),
+ fa_wheelchair('\uf193'),
+ fa_vimeo_square('\uf194'),
+ fa_turkish_lira('\uf195'),
+ fa_try('\uf195'),
+ fa_plus_square_o('\uf196'),
+ fa_space_shuttle('\uf197'),
+ fa_slack('\uf198'),
+ fa_envelope_square('\uf199'),
+ fa_wordpress('\uf19a'),
+ fa_openid('\uf19b'),
+ fa_institution('\uf19c'),
+ fa_bank('\uf19c'),
+ fa_university('\uf19c'),
+ fa_mortar_board('\uf19d'),
+ fa_graduation_cap('\uf19d'),
+ fa_yahoo('\uf19e'),
+ fa_google('\uf1a0'),
+ fa_reddit('\uf1a1'),
+ fa_reddit_square('\uf1a2'),
+ fa_stumbleupon_circle('\uf1a3'),
+ fa_stumbleupon('\uf1a4'),
+ fa_delicious('\uf1a5'),
+ fa_digg('\uf1a6'),
+ fa_pied_piper('\uf1a7'),
+ fa_pied_piper_alt('\uf1a8'),
+ fa_drupal('\uf1a9'),
+ fa_joomla('\uf1aa'),
+ fa_language('\uf1ab'),
+ fa_fax('\uf1ac'),
+ fa_building('\uf1ad'),
+ fa_child('\uf1ae'),
+ fa_paw('\uf1b0'),
+ fa_spoon('\uf1b1'),
+ fa_cube('\uf1b2'),
+ fa_cubes('\uf1b3'),
+ fa_behance('\uf1b4'),
+ fa_behance_square('\uf1b5'),
+ fa_steam('\uf1b6'),
+ fa_steam_square('\uf1b7'),
+ fa_recycle('\uf1b8'),
+ fa_automobile('\uf1b9'),
+ fa_car('\uf1b9'),
+ fa_cab('\uf1ba'),
+ fa_taxi('\uf1ba'),
+ fa_tree('\uf1bb'),
+ fa_spotify('\uf1bc'),
+ fa_deviantart('\uf1bd'),
+ fa_soundcloud('\uf1be'),
+ fa_database('\uf1c0'),
+ fa_file_pdf_o('\uf1c1'),
+ fa_file_word_o('\uf1c2'),
+ fa_file_excel_o('\uf1c3'),
+ fa_file_powerpoint_o('\uf1c4'),
+ fa_file_photo_o('\uf1c5'),
+ fa_file_picture_o('\uf1c5'),
+ fa_file_image_o('\uf1c5'),
+ fa_file_zip_o('\uf1c6'),
+ fa_file_archive_o('\uf1c6'),
+ fa_file_sound_o('\uf1c7'),
+ fa_file_audio_o('\uf1c7'),
+ fa_file_movie_o('\uf1c8'),
+ fa_file_video_o('\uf1c8'),
+ fa_file_code_o('\uf1c9'),
+ fa_vine('\uf1ca'),
+ fa_codepen('\uf1cb'),
+ fa_jsfiddle('\uf1cc'),
+ fa_life_bouy('\uf1cd'),
+ fa_life_buoy('\uf1cd'),
+ fa_life_saver('\uf1cd'),
+ fa_support('\uf1cd'),
+ fa_life_ring('\uf1cd'),
+ fa_circle_o_notch('\uf1ce'),
+ fa_ra('\uf1d0'),
+ fa_rebel('\uf1d0'),
+ fa_ge('\uf1d1'),
+ fa_empire('\uf1d1'),
+ fa_git_square('\uf1d2'),
+ fa_git('\uf1d3'),
+ fa_y_combinator_square('\uf1d4'),
+ fa_yc_square('\uf1d4'),
+ fa_hacker_news('\uf1d4'),
+ fa_tencent_weibo('\uf1d5'),
+ fa_qq('\uf1d6'),
+ fa_wechat('\uf1d7'),
+ fa_weixin('\uf1d7'),
+ fa_send('\uf1d8'),
+ fa_paper_plane('\uf1d8'),
+ fa_send_o('\uf1d9'),
+ fa_paper_plane_o('\uf1d9'),
+ fa_history('\uf1da'),
+ fa_circle_thin('\uf1db'),
+ fa_header('\uf1dc'),
+ fa_paragraph('\uf1dd'),
+ fa_sliders('\uf1de'),
+ fa_share_alt('\uf1e0'),
+ fa_share_alt_square('\uf1e1'),
+ fa_bomb('\uf1e2'),
+ fa_soccer_ball_o('\uf1e3'),
+ fa_futbol_o('\uf1e3'),
+ fa_tty('\uf1e4'),
+ fa_binoculars('\uf1e5'),
+ fa_plug('\uf1e6'),
+ fa_slideshare('\uf1e7'),
+ fa_twitch('\uf1e8'),
+ fa_yelp('\uf1e9'),
+ fa_newspaper_o('\uf1ea'),
+ fa_wifi('\uf1eb'),
+ fa_calculator('\uf1ec'),
+ fa_paypal('\uf1ed'),
+ fa_google_wallet('\uf1ee'),
+ fa_cc_visa('\uf1f0'),
+ fa_cc_mastercard('\uf1f1'),
+ fa_cc_discover('\uf1f2'),
+ fa_cc_amex('\uf1f3'),
+ fa_cc_paypal('\uf1f4'),
+ fa_cc_stripe('\uf1f5'),
+ fa_bell_slash('\uf1f6'),
+ fa_bell_slash_o('\uf1f7'),
+ fa_trash('\uf1f8'),
+ fa_copyright('\uf1f9'),
+ fa_at('\uf1fa'),
+ fa_eyedropper('\uf1fb'),
+ fa_paint_brush('\uf1fc'),
+ fa_birthday_cake('\uf1fd'),
+ fa_area_chart('\uf1fe'),
+ fa_pie_chart('\uf200'),
+ fa_line_chart('\uf201'),
+ fa_lastfm('\uf202'),
+ fa_lastfm_square('\uf203'),
+ fa_toggle_off('\uf204'),
+ fa_toggle_on('\uf205'),
+ fa_bicycle('\uf206'),
+ fa_bus('\uf207'),
+ fa_ioxhost('\uf208'),
+ fa_angellist('\uf209'),
+ fa_cc('\uf20a'),
+ fa_shekel('\uf20b'),
+ fa_sheqel('\uf20b'),
+ fa_ils('\uf20b'),
+ fa_meanpath('\uf20c'),
+ fa_buysellads('\uf20d'),
+ fa_connectdevelop('\uf20e'),
+ fa_dashcube('\uf210'),
+ fa_forumbee('\uf211'),
+ fa_leanpub('\uf212'),
+ fa_sellsy('\uf213'),
+ fa_shirtsinbulk('\uf214'),
+ fa_simplybuilt('\uf215'),
+ fa_skyatlas('\uf216'),
+ fa_cart_plus('\uf217'),
+ fa_cart_arrow_down('\uf218'),
+ fa_diamond('\uf219'),
+ fa_ship('\uf21a'),
+ fa_user_secret('\uf21b'),
+ fa_motorcycle('\uf21c'),
+ fa_street_view('\uf21d'),
+ fa_heartbeat('\uf21e'),
+ fa_venus('\uf221'),
+ fa_mars('\uf222'),
+ fa_mercury('\uf223'),
+ fa_intersex('\uf224'),
+ fa_transgender('\uf224'),
+ fa_transgender_alt('\uf225'),
+ fa_venus_double('\uf226'),
+ fa_mars_double('\uf227'),
+ fa_venus_mars('\uf228'),
+ fa_mars_stroke('\uf229'),
+ fa_mars_stroke_v('\uf22a'),
+ fa_mars_stroke_h('\uf22b'),
+ fa_neuter('\uf22c'),
+ fa_genderless('\uf22d'),
+ fa_facebook_official('\uf230'),
+ fa_pinterest_p('\uf231'),
+ fa_whatsapp('\uf232'),
+ fa_server('\uf233'),
+ fa_user_plus('\uf234'),
+ fa_user_times('\uf235'),
+ fa_hotel('\uf236'),
+ fa_bed('\uf236'),
+ fa_viacoin('\uf237'),
+ fa_train('\uf238'),
+ fa_subway('\uf239'),
+ fa_medium('\uf23a'),
+ fa_yc('\uf23b'),
+ fa_y_combinator('\uf23b'),
+ fa_optin_monster('\uf23c'),
+ fa_opencart('\uf23d'),
+ fa_expeditedssl('\uf23e'),
+ fa_battery_4('\uf240'),
+ fa_battery_full('\uf240'),
+ fa_battery_3('\uf241'),
+ fa_battery_three_quarters('\uf241'),
+ fa_battery_2('\uf242'),
+ fa_battery_half('\uf242'),
+ fa_battery_1('\uf243'),
+ fa_battery_quarter('\uf243'),
+ fa_battery_0('\uf244'),
+ fa_battery_empty('\uf244'),
+ fa_mouse_pointer('\uf245'),
+ fa_i_cursor('\uf246'),
+ fa_object_group('\uf247'),
+ fa_object_ungroup('\uf248'),
+ fa_sticky_note('\uf249'),
+ fa_sticky_note_o('\uf24a'),
+ fa_cc_jcb('\uf24b'),
+ fa_cc_diners_club('\uf24c'),
+ fa_clone('\uf24d'),
+ fa_balance_scale('\uf24e'),
+ fa_hourglass_o('\uf250'),
+ fa_hourglass_1('\uf251'),
+ fa_hourglass_start('\uf251'),
+ fa_hourglass_2('\uf252'),
+ fa_hourglass_half('\uf252'),
+ fa_hourglass_3('\uf253'),
+ fa_hourglass_end('\uf253'),
+ fa_hourglass('\uf254'),
+ fa_hand_grab_o('\uf255'),
+ fa_hand_rock_o('\uf255'),
+ fa_hand_stop_o('\uf256'),
+ fa_hand_paper_o('\uf256'),
+ fa_hand_scissors_o('\uf257'),
+ fa_hand_lizard_o('\uf258'),
+ fa_hand_spock_o('\uf259'),
+ fa_hand_pointer_o('\uf25a'),
+ fa_hand_peace_o('\uf25b'),
+ fa_trademark('\uf25c'),
+ fa_registered('\uf25d'),
+ fa_creative_commons('\uf25e'),
+ fa_gg('\uf260'),
+ fa_gg_circle('\uf261'),
+ fa_tripadvisor('\uf262'),
+ fa_odnoklassniki('\uf263'),
+ fa_odnoklassniki_square('\uf264'),
+ fa_get_pocket('\uf265'),
+ fa_wikipedia_w('\uf266'),
+ fa_safari('\uf267'),
+ fa_chrome('\uf268'),
+ fa_firefox('\uf269'),
+ fa_opera('\uf26a'),
+ fa_internet_explorer('\uf26b'),
+ fa_tv('\uf26c'),
+ fa_television('\uf26c'),
+ fa_contao('\uf26d'),
+ fa_500px('\uf26e'),
+ fa_amazon('\uf270'),
+ fa_calendar_plus_o('\uf271'),
+ fa_calendar_minus_o('\uf272'),
+ fa_calendar_times_o('\uf273'),
+ fa_calendar_check_o('\uf274'),
+ fa_industry('\uf275'),
+ fa_map_pin('\uf276'),
+ fa_map_signs('\uf277'),
+ fa_map_o('\uf278'),
+ fa_map('\uf279'),
+ fa_commenting('\uf27a'),
+ fa_commenting_o('\uf27b'),
+ fa_houzz('\uf27c'),
+ fa_vimeo('\uf27d'),
+ fa_black_tie('\uf27e'),
+ fa_fonticons('\uf280'),
+ fa_reddit_alien('\uf281'),
+ fa_edge('\uf282'),
+ fa_credit_card_alt('\uf283'),
+ fa_codiepie('\uf284'),
+ fa_modx('\uf285'),
+ fa_fort_awesome('\uf286'),
+ fa_usb('\uf287'),
+ fa_product_hunt('\uf288'),
+ fa_mixcloud('\uf289'),
+ fa_scribd('\uf28a'),
+ fa_pause_circle('\uf28b'),
+ fa_pause_circle_o('\uf28c'),
+ fa_stop_circle('\uf28d'),
+ fa_stop_circle_o('\uf28e'),
+ fa_shopping_bag('\uf290'),
+ fa_shopping_basket('\uf291'),
+ fa_hashtag('\uf292'),
+ fa_bluetooth('\uf293'),
+ fa_bluetooth_b('\uf294'),
+ fa_percent('\uf295');
+
+ char character;
+
+ FontAwesomeIcons(char character) {
+ this.character = character;
+ }
+
+ @Override
+ public String key() {
+ return name().replace('_', '-');
+ }
+
+ @Override
+ public char character() {
+ return character;
+ }
+}
diff --git a/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeModule.java b/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeModule.java
new file mode 100644
index 0000000000..0f2dfce4d4
--- /dev/null
+++ b/iconify/src/main/java/com/joanzapata/iconify/fonts/FontAwesomeModule.java
@@ -0,0 +1,17 @@
+package com.joanzapata.iconify.fonts;
+
+import com.joanzapata.iconify.Icon;
+import com.joanzapata.iconify.IconFontDescriptor;
+
+public class FontAwesomeModule implements IconFontDescriptor {
+
+ @Override
+ public String ttfFileName() {
+ return "iconify/android-iconify-fontawesome.ttf";
+ }
+
+ @Override
+ public Icon[] characters() {
+ return FontAwesomeIcons.values();
+ }
+}
diff --git a/iconify/src/main/java/com/joanzapata/iconify/internal/CustomTypefaceSpan.java b/iconify/src/main/java/com/joanzapata/iconify/internal/CustomTypefaceSpan.java
new file mode 100644
index 0000000000..a07bb49ef3
--- /dev/null
+++ b/iconify/src/main/java/com/joanzapata/iconify/internal/CustomTypefaceSpan.java
@@ -0,0 +1,88 @@
+package com.joanzapata.iconify.internal;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.text.style.ReplacementSpan;
+import com.joanzapata.iconify.Icon;
+
+public class CustomTypefaceSpan extends ReplacementSpan {
+ private static final int ROTATION_DURATION = 2000;
+ private static final Rect TEXT_BOUNDS = new Rect();
+ private static final Paint LOCAL_PAINT = new Paint();
+ private static final float BASELINE_RATIO = 1 / 7f;
+
+ private final String icon;
+ private final Typeface type;
+ private final float iconSizePx;
+ private final float iconSizeRatio;
+ private final int iconColor;
+ private final boolean rotate;
+ private final boolean baselineAligned;
+ private final long rotationStartTime;
+
+ public CustomTypefaceSpan(Icon icon, Typeface type,
+ float iconSizePx, float iconSizeRatio, int iconColor,
+ boolean rotate, boolean baselineAligned) {
+ this.rotate = rotate;
+ this.baselineAligned = baselineAligned;
+ this.icon = String.valueOf(icon.character());
+ this.type = type;
+ this.iconSizePx = iconSizePx;
+ this.iconSizeRatio = iconSizeRatio;
+ this.iconColor = iconColor;
+ this.rotationStartTime = System.currentTimeMillis();
+ }
+
+ @Override
+ public int getSize(Paint paint, CharSequence text,
+ int start, int end, Paint.FontMetricsInt fm) {
+ LOCAL_PAINT.set(paint);
+ applyCustomTypeFace(LOCAL_PAINT, type);
+ LOCAL_PAINT.getTextBounds(icon, 0, 1, TEXT_BOUNDS);
+ if (fm != null) {
+ float baselineRatio = baselineAligned ? 0 : BASELINE_RATIO;
+ fm.descent = (int) (TEXT_BOUNDS.height() * baselineRatio);
+ fm.ascent = -(TEXT_BOUNDS.height() - fm.descent);
+ fm.top = fm.ascent;
+ fm.bottom = fm.descent;
+ }
+ return TEXT_BOUNDS.width();
+ }
+
+ @Override
+ public void draw(Canvas canvas, CharSequence text,
+ int start, int end, float x, int top, int y,
+ int bottom, Paint paint) {
+ applyCustomTypeFace(paint, type);
+ paint.getTextBounds(icon, 0, 1, TEXT_BOUNDS);
+ canvas.save();
+ float baselineRatio = baselineAligned ? 0f : BASELINE_RATIO;
+ if (rotate) {
+ float rotation = (System.currentTimeMillis() - rotationStartTime) / (float) ROTATION_DURATION * 360f;
+ float centerX = x + TEXT_BOUNDS.width() / 2f;
+ float centerY = y - TEXT_BOUNDS.height() / 2f + TEXT_BOUNDS.height() * baselineRatio;
+ canvas.rotate(rotation, centerX, centerY);
+ }
+
+ canvas.drawText(icon,
+ x - TEXT_BOUNDS.left,
+ y - TEXT_BOUNDS.bottom + TEXT_BOUNDS.height() * baselineRatio, paint);
+ canvas.restore();
+ }
+
+ public boolean isAnimated() {
+ return rotate;
+ }
+
+ private void applyCustomTypeFace(Paint paint, Typeface tf) {
+ paint.setFakeBoldText(false);
+ paint.setTextSkewX(0f);
+ paint.setTypeface(tf);
+ if (rotate) paint.clearShadowLayer();
+ if (iconSizeRatio > 0) paint.setTextSize(paint.getTextSize() * iconSizeRatio);
+ else if (iconSizePx > 0) paint.setTextSize(iconSizePx);
+ if (iconColor < Integer.MAX_VALUE) paint.setColor(iconColor);
+ }
+}
\ No newline at end of file
diff --git a/iconify/src/main/java/com/joanzapata/iconify/internal/HasOnViewAttachListener.java b/iconify/src/main/java/com/joanzapata/iconify/internal/HasOnViewAttachListener.java
new file mode 100644
index 0000000000..773c2a6949
--- /dev/null
+++ b/iconify/src/main/java/com/joanzapata/iconify/internal/HasOnViewAttachListener.java
@@ -0,0 +1,82 @@
+package com.joanzapata.iconify.internal;
+
+import android.widget.TextView;
+
+import androidx.core.view.ViewCompat;
+
+/**
+ * Any TextView subclass that wishes to call {@link com.joanzapata.iconify.Iconify#addIcons(TextView...)} on it
+ * needs to implement this interface if it ever want to use spinning icons.
+ *
+ * IconTextView, IconButton and IconToggleButton already implement it, but if you need to implement it
+ * yourself, please use {@link com.joanzapata.iconify.internal.HasOnViewAttachListener.HasOnViewAttachListenerDelegate}
+ * to help you.
+ */
+public interface HasOnViewAttachListener {
+ void setOnViewAttachListener(OnViewAttachListener listener);
+
+ interface OnViewAttachListener {
+ void onAttach();
+
+ void onDetach();
+ }
+
+ /**
+ * Helper class to implement {@link HasOnViewAttachListener}.
+ * Usual implementation should look like this:
+ *
+ * {@code + * class MyClass extends TextView implements HasOnViewAttachListener { + * + * private HasOnViewAttachListenerDelegate delegate + * + * @Override + * public void setOnViewAttachListener(OnViewAttachListener listener) { + * if (delegate == null) delegate = new HasOnViewAttachListenerDelegate(this); + * delegate.setOnViewAttachListener(listener); + * } + * + * @Override + * protected void onAttachedToWindow() { + * super.onAttachedToWindow(); + * delegate.onAttachedToWindow(); + * } + * + * @Override + * protected void onDetachedFromWindow() { + * super.onDetachedFromWindow(); + * delegate.onDetachedFromWindow(); + * } + * + * } + * } + *+ */ + class HasOnViewAttachListenerDelegate { + + private final TextView view; + private OnViewAttachListener listener; + + public HasOnViewAttachListenerDelegate(TextView view) { + this.view = view; + } + + public void setOnViewAttachListener(OnViewAttachListener listener) { + if (this.listener != null) + this.listener.onDetach(); + this.listener = listener; + if (ViewCompat.isAttachedToWindow(view) && listener != null) { + listener.onAttach(); + } + } + + public void onAttachedToWindow() { + if (listener != null) listener.onAttach(); + } + + public void onDetachedFromWindow() { + if (listener != null) listener.onDetach(); + } + + } +} \ No newline at end of file diff --git a/iconify/src/main/java/com/joanzapata/iconify/internal/IconFontDescriptorWrapper.java b/iconify/src/main/java/com/joanzapata/iconify/internal/IconFontDescriptorWrapper.java new file mode 100644 index 0000000000..b7e1d0045b --- /dev/null +++ b/iconify/src/main/java/com/joanzapata/iconify/internal/IconFontDescriptorWrapper.java @@ -0,0 +1,49 @@ +package com.joanzapata.iconify.internal; + +import android.content.Context; +import android.graphics.Typeface; +import com.joanzapata.iconify.Icon; +import com.joanzapata.iconify.IconFontDescriptor; + +import java.util.HashMap; +import java.util.Map; + +public class IconFontDescriptorWrapper { + + private final IconFontDescriptor iconFontDescriptor; + + private final Map