org.getlantern.firetweet.util.ThemeUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.getlantern.firetweet.util.ThemeUtils.java

Source

/*
 *             Firetweet - Twitter client for Android
 * 
 *  Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.getlantern.firetweet.util;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewCompat;
import android.support.v7.internal.app.WindowDecorActionBar;
import android.support.v7.internal.app.WindowDecorActionBar.ActionModeImpl;
import android.support.v7.internal.view.SupportActionModeWrapper;
import android.support.v7.internal.view.SupportActionModeWrapperTrojan;
import android.support.v7.internal.view.menu.ActionMenuItemView;
import android.support.v7.internal.widget.ActionBarOverlayLayout;
import android.support.v7.widget.ActionMenuView;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
import android.widget.TextView;

import org.apache.commons.lang3.ArrayUtils;
import org.getlantern.firetweet.Constants;
import org.getlantern.firetweet.R;
import org.getlantern.firetweet.activity.iface.IThemedActivity;
import org.getlantern.firetweet.graphic.ActionBarColorDrawable;
import org.getlantern.firetweet.graphic.ActionIconDrawable;
import org.getlantern.firetweet.text.ParagraphSpacingSpan;
import org.getlantern.firetweet.util.accessor.ViewAccessor;
import org.getlantern.firetweet.util.menu.FiretweetMenuInfo;
import org.getlantern.firetweet.view.TabPagerIndicator;
import org.getlantern.firetweet.view.iface.IThemedView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import com.crashlytics.android.Crashlytics;

public class ThemeUtils implements Constants {

    private static final int[] ANIM_OPEN_STYLE_ATTRS = { android.R.attr.activityOpenEnterAnimation,
            android.R.attr.activityOpenExitAnimation };
    private static final int[] ANIM_CLOSE_STYLE_ATTRS = { android.R.attr.activityCloseEnterAnimation,
            android.R.attr.activityCloseExitAnimation };

    private static final String[] sClassPrefixList = { "android.widget.", "android.webkit.",
            "org.getlantern.firetweet.view" };

    private ThemeUtils() {
        throw new AssertionError();
    }

    public static void applyActionBarBackground(final ActionBar actionBar, final Context context,
            final int themeRes) {
        if (actionBar == null || context == null)
            return;
        actionBar.setBackgroundDrawable(getActionBarBackground(context, themeRes));
        actionBar.setSplitBackgroundDrawable(getActionBarSplitBackground(context, themeRes));
        //        actionBar.setStackedBackgroundDrawable(getActionBarStackedBackground(context, themeRes));
    }

    public static void applyActionBarBackground(final ActionBar actionBar, final Context context,
            final int themeRes, final int accentColor, boolean outlineEnabled) {
        if (actionBar == null || context == null)
            return;
        actionBar.setBackgroundDrawable(getActionBarBackground(context, themeRes, accentColor, outlineEnabled));
        actionBar.setSplitBackgroundDrawable(getActionBarSplitBackground(context, themeRes));
        actionBar.setStackedBackgroundDrawable(
                getActionBarBackground(context, themeRes, accentColor, outlineEnabled));
    }

    public static void applyActionBarBackground(final android.support.v7.app.ActionBar actionBar,
            final Context context, final int themeRes, final int accentColor, boolean outlineEnabled) {
        if (actionBar == null || context == null)
            return;
        actionBar.setBackgroundDrawable(getActionBarBackground(context, themeRes, accentColor, outlineEnabled));
        actionBar.setSplitBackgroundDrawable(getActionBarSplitBackground(context, themeRes));
        actionBar.setStackedBackgroundDrawable(
                getActionBarStackedBackground(context, themeRes, accentColor, outlineEnabled));
    }

    public static void applyBackground(final View view) {
        if (view == null)
            return;
        applyBackground(view, getUserAccentColor(view.getContext()));
    }

    public static void applyBackground(final View view, final int color) {
        if (view == null)
            return;
        try {
            final Drawable bg = view.getBackground();
            if (bg == null)
                return;
            final Drawable mutated = bg.mutate();
            if (mutated == null)
                return;
            mutated.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
            view.invalidate();
        } catch (final Exception e) {
            Crashlytics.logException(e);
            e.printStackTrace();
        }
    }

    public static void applySupportActionModeBackground(ActionMode mode, FragmentActivity activity, int themeRes,
            int accentColor, boolean outlineEnabled) {
        // Very dirty implementation
        if (!(mode instanceof SupportActionModeWrapper) || !(activity instanceof IThemedActivity))
            return;
        final android.support.v7.view.ActionMode modeCompat = SupportActionModeWrapperTrojan
                .getWrappedObject((SupportActionModeWrapper) mode);
        if (!(modeCompat instanceof ActionModeImpl))
            return;
        try {
            WindowDecorActionBar actionBar = null;
            final Field[] fields = ActionModeImpl.class.getDeclaredFields();
            for (Field field : fields) {
                if (WindowDecorActionBar.class.isAssignableFrom(field.getType())) {
                    field.setAccessible(true);
                    actionBar = (WindowDecorActionBar) field.get(modeCompat);
                    break;
                }
            }
            if (actionBar == null)
                return;
            final Field contextViewField = WindowDecorActionBar.class.getDeclaredField("mContextView");
            contextViewField.setAccessible(true);
            final View view = (View) contextViewField.get(actionBar);
            if (view == null)
                return;
            ViewAccessor.setBackground(view,
                    getActionBarBackground(activity, themeRes, accentColor, outlineEnabled));
        } catch (Exception e) {
            Crashlytics.logException(e);
            e.printStackTrace();
        }
    }

    public static Drawable getCompatToolbarOverlay(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            return null;
        final View view = activity.getWindow().findViewById(android.support.v7.appcompat.R.id.decor_content_parent);
        if (!(view instanceof ActionBarOverlayLayout))
            return null;
        try {
            final Field field = ActionBarOverlayLayout.class.getDeclaredField("mWindowContentOverlay");
            field.setAccessible(true);
            return (Drawable) field.get(view);
        } catch (Exception ignore) {
        }
        return null;
    }

    public static void initPagerIndicatorAsActionBarTab(FragmentActivity activity, TabPagerIndicator indicator) {
        final float supportActionBarElevation = getSupportActionBarElevation(activity);
        ViewCompat.setElevation(indicator, supportActionBarElevation);
        if (!(activity instanceof IThemedActivity))
            return;
        final int themeRes = ((IThemedActivity) activity).getCurrentThemeResourceId();
        final int themeColor = ((IThemedActivity) activity).getCurrentThemeColor();
        final int contrastColor = ColorUtils.getContrastYIQ(themeColor, 192);
        ViewAccessor.setBackground(indicator, getActionBarStackedBackground(activity, themeRes, themeColor, true));
        if (isDarkTheme(themeRes)) {
            final int foregroundColor = getThemeForegroundColor(activity);
            indicator.setIconColor(foregroundColor);
            indicator.setLabelColor(foregroundColor);
            indicator.setStripColor(themeColor);
        } else {
            indicator.setIconColor(contrastColor);
            indicator.setLabelColor(contrastColor);
            indicator.setStripColor(contrastColor);
        }
        indicator.updateAppearance();
    }

    public static void resetCheatSheet(ActionMenuView menuView) {
        final OnLongClickListener listener = new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (((ActionMenuItemView) v).hasText())
                    return false;
                final MenuItem menuItem = ((ActionMenuItemView) v).getItemData();
                Utils.showMenuItemToast(v, menuItem.getTitle(), true);
                return true;
            }
        };
        for (int i = 0, j = menuView.getChildCount(); i < j; i++) {
            final View child = menuView.getChildAt(i);
            if (!(child instanceof ActionMenuItemView))
                continue;
            final ActionMenuItemView itemView = (ActionMenuItemView) child;
            final MenuItem menuItem = itemView.getItemData();
            if (menuItem.hasSubMenu())
                continue;
            itemView.setOnLongClickListener(listener);
        }
    }

    public static void setCompatToolbarOverlay(Activity activity, Drawable background) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            return;
        final View view = activity.getWindow().findViewById(android.support.v7.appcompat.R.id.decor_content_parent);
        if (!(view instanceof ActionBarOverlayLayout))
            return;
        try {
            final Field field = ActionBarOverlayLayout.class.getDeclaredField("mWindowContentOverlay");
            field.setAccessible(true);
            field.set(view, background);
        } catch (Exception ignore) {
        }
    }

    public static void wrapMenuIcon(@NonNull Menu menu, int itemColor, int subItemColor, int... excludeGroups) {
        for (int i = 0, j = menu.size(); i < j; i++) {
            final MenuItem item = menu.getItem(i);
            wrapMenuItemIcon(item, itemColor, excludeGroups);
            if (item.hasSubMenu()) {
                wrapMenuIcon(item.getSubMenu(), subItemColor, subItemColor, excludeGroups);
            }
        }
    }

    public static void wrapMenuItemIcon(@NonNull MenuItem item, int itemColor, int... excludeGroups) {
        if (ArrayUtils.contains(excludeGroups, item.getGroupId()))
            return;
        final Drawable icon = item.getIcon();
        if (icon == null)
            return;
        icon.mutate();
        final Drawable.Callback callback = icon.getCallback();
        final ActionIconDrawable newIcon = new ActionIconDrawable(icon, itemColor);
        newIcon.setCallback(callback);
        item.setIcon(newIcon);
    }

    public static void wrapMenuIcon(ActionMenuView view, int... excludeGroups) {
        final int itemBackgroundColor = ThemeUtils.getThemeBackgroundColor(view.getContext());
        final int popupItemBackgroundColor = ThemeUtils.getThemeBackgroundColor(view.getContext(),
                view.getPopupTheme());
        final Resources resources = view.getResources();
        final int colorDark = resources.getColor(R.color.action_icon_dark);
        final int colorLight = resources.getColor(R.color.action_icon_light);
        final int itemColor = ColorUtils.getContrastYIQ(itemBackgroundColor, colorDark, colorLight);
        final int popupItemColor = ColorUtils.getContrastYIQ(popupItemBackgroundColor, colorDark, colorLight);
        final Menu menu = view.getMenu();
        final int childCount = view.getChildCount();
        for (int i = 0, j = menu.size(), k = 0; i < j; i++) {
            final MenuItem item = menu.getItem(i);
            wrapMenuItemIcon(item, itemColor, excludeGroups);
            if (item.hasSubMenu()) {
                wrapMenuIcon(menu, popupItemColor, popupItemColor, excludeGroups);
            }
            if (item.isVisible()) {
                k++;
            }
        }
    }

    public static void wrapMenuIcon(Context context, Menu menu, int... excludeGroups) {
        final int backgroundColor = ThemeUtils.getThemeBackgroundColor(context);
        wrapMenuIcon(context, backgroundColor, backgroundColor, menu, excludeGroups);
    }

    public static void wrapMenuIcon(Context context, int backgroundColor, int popupBackgroundColor, Menu menu,
            int... excludeGroups) {
        final Resources resources = context.getResources();
        final int colorDark = resources.getColor(R.color.action_icon_dark);
        final int colorLight = resources.getColor(R.color.action_icon_light);
        final int itemColor = ColorUtils.getContrastYIQ(backgroundColor, colorDark, colorLight);
        final int popupItemColor = ColorUtils.getContrastYIQ(popupBackgroundColor, colorDark, colorLight);
        for (int i = 0, j = menu.size(), k = 0; i < j; i++) {
            final MenuItem item = menu.getItem(i);
            wrapMenuItemIcon(item, itemColor, excludeGroups);
            if (item.hasSubMenu()) {
                wrapMenuIcon(menu, popupItemColor, popupItemColor, excludeGroups);
            }
            if (item.isVisible()) {
                k++;
            }
        }
    }

    public static void applyThemeAlphaToDrawable(final Context context, final Drawable d) {
        if (context == null || d == null)
            return;
        d.setAlpha(getThemeAlpha(getThemeResource(context)));
    }

    public static void applyThemeBackgroundAlphaToDrawable(final Context context, final Drawable d) {
        if (context == null || d == null)
            return;
        d.setAlpha(getUserThemeBackgroundAlpha(context));
    }

    public static int getActionBarPopupThemeRes(final Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
            return 0;
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.actionBarPopupTheme });
        try {
            return a.getResourceId(0, 0);
        } finally {
            a.recycle();
        }
    }

    public static void applyColorFilterToMenuIcon(Activity activity, Menu menu) {
        final ActionBar actionBar = activity.getActionBar();
        final Context context = actionBar != null ? actionBar.getThemedContext() : activity;
        final int color = getThemeForegroundColor(context);
        final int popupTheme = getActionBarPopupThemeRes(context);
        final int popupColor = getThemeForegroundColor(context, popupTheme);
        final int highlightColor = getUserAccentColor(activity);
        applyColorFilterToMenuIcon(menu, color, popupColor, highlightColor, Mode.SRC_ATOP);
    }

    public static void applyColorFilterToMenuIcon(final Menu menu, final int color, final int highlightColor,
            final Mode mode, final int... excludedGroups) {
        applyColorFilterToMenuIcon(menu, color, color, highlightColor, mode, excludedGroups);
    }

    public static void applyColorFilterToMenuIcon(final Menu menu, final int color, final int popupColor,
            final int highlightColor, final Mode mode, final int... excludedGroups) {
        for (int i = 0, j = menu.size(); i < j; i++) {
            final MenuItem item = menu.getItem(i);
            final Drawable icon = item.getIcon();
            final ContextMenuInfo info = item.getMenuInfo();
            if (icon != null && !ArrayUtils.contains(excludedGroups, item.getGroupId())) {
                icon.mutate();
                if (info instanceof FiretweetMenuInfo) {
                    final FiretweetMenuInfo sInfo = (FiretweetMenuInfo) info;
                    icon.setColorFilter(sInfo.isHighlight() ? sInfo.getHighlightColor(highlightColor) : color,
                            mode);
                } else {
                    icon.setColorFilter(color, mode);
                }
            }
            if (item.hasSubMenu()) {
                // SubMenu item is always in popup
                applyColorFilterToMenuIcon(item.getSubMenu(), popupColor, popupColor, highlightColor, mode,
                        excludedGroups);
            }
        }
    }

    public static View createView(final String name, final Context context, final AttributeSet attrs) {
        return createView(name, context, attrs, 0);
    }

    public static View createView(final String name, final Context context, final AttributeSet attrs,
            final int tintColor) {
        View view = null;
        try {
            view = newViewInstance(name, context, attrs);
        } catch (final Exception e) {
            Crashlytics.logException(e);
            // In this case we want to let the base class take a crack
            // at it.
        }
        for (final String prefix : sClassPrefixList) {
            try {
                view = newViewInstance(prefix + name, context, attrs);
            } catch (final Exception e) {
                Crashlytics.logException(e);
                // In this case we want to let the base class take a crack
                // at it.
            }
        }
        if (view != null) {
            applyColorTintForView(view, tintColor);
        }
        return view;
    }

    public static int getOptimalLinkColor(int linkColor, int color) {
        final int[] yiq = new int[3];
        ColorUtils.colorToYIQ(color, yiq);
        final int y = yiq[0];
        ColorUtils.colorToYIQ(linkColor, yiq);
        if (y < 32 && yiq[0] < 192) {
            return linkColor;
        } else if (y > 192 && yiq[0] > 32) {
            return linkColor;
        }
        yiq[0] = yiq[0] + (y - yiq[0]) / 2;
        return ColorUtils.YIQToColor(Color.alpha(linkColor), yiq);
    }

    public static int getQuickSearchBarThemeResource(final Context context) {
        return getQuickSearchBarThemeResource(getThemeNameOption(context));
    }

    public static int getQuickSearchBarThemeResource(final String name) {
        if (VALUE_THEME_NAME_DARK.equals(name))
            return R.style.Theme_Firetweet_Dark_QuickSearchBar;
        return R.style.Theme_Firetweet_Light_QuickSearchBar;
    }

    private static void applyColorTintForView(View view, int tintColor) {
        if (view instanceof IThemedView) {
            final ColorStateList tintList = ColorStateList.valueOf(tintColor);
            ((IThemedView) view).setThemeTintColor(tintList);
        } else if (view instanceof ProgressBar) {
            final ColorStateList tintList = ColorStateList.valueOf(tintColor);
            final ProgressBar progressBar = (ProgressBar) view;
            ViewAccessor.setProgressTintList(progressBar, tintList);
            ViewAccessor.setProgressBackgroundTintList(progressBar, tintList);
            ViewAccessor.setIndeterminateTintList(progressBar, tintList);
        } else if (view instanceof CompoundButton) {
            final ColorStateList tintList = ColorStateList.valueOf(tintColor);
            final CompoundButton compoundButton = (CompoundButton) view;
            ViewAccessor.setButtonTintList(compoundButton, tintList);
        }
        // TODO support TintableBackgroundView
    }

    public static float getSupportActionBarElevation(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { R.attr.elevation },
                R.attr.actionBarStyle, 0);
        try {
            return a.getDimension(0, 0);
        } finally {
            a.recycle();
        }
    }

    public static float getActionBarElevation(final Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
            return 0;
        @SuppressLint("InlinedApi")
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { android.R.attr.elevation },
                android.R.attr.actionBarStyle, 0);
        try {
            return a.getDimension(0, 0);
        } finally {
            a.recycle();
        }
    }

    public static Drawable getActionBarBackground(final Context context, final int themeRes) {
        final TypedArray a1 = context.obtainStyledAttributes(null, new int[] { android.R.attr.background },
                R.attr.actionBarStyle, themeRes);
        try {
            if (a1.hasValue(0)) {
                return applyActionBarDrawable(context, a1.getDrawable(0), isTransparentBackground(themeRes));
            }
        } finally {
            a1.recycle();
        }
        final TypedArray a2 = context.obtainStyledAttributes(null, new int[] { android.R.attr.background },
                android.R.attr.actionBarStyle, themeRes);
        try {
            if (a2.hasValue(0)) {
                return applyActionBarDrawable(context, a2.getDrawable(0), isTransparentBackground(themeRes));
            }
        } finally {
            a2.recycle();
        }
        return null;
    }

    @NonNull
    public static Drawable getActionBarBackground(final Context context, final int themeRes, final int accentColor,
            boolean outlineEnabled) {
        final int actionBarColor;
        if (isDarkTheme(themeRes)) {
            actionBarColor = context.getResources().getColor(R.color.background_color_action_bar_dark);
        } else {
            actionBarColor = accentColor;
        }
        final ColorDrawable d = new ActionBarColorDrawable(actionBarColor, outlineEnabled);
        return applyActionBarDrawable(context, d, isTransparentBackground(themeRes));
    }

    @NonNull
    public static Drawable getActionBarStackedBackground(final Context context, final int themeRes,
            final int accentColor, boolean outlineEnabled) {
        final int actionBarColor;
        if (isDarkTheme(themeRes)) {
            actionBarColor = context.getResources().getColor(R.color.background_color_action_bar_dark);
        } else {
            actionBarColor = accentColor;
        }
        final ColorDrawable d = new ActionBarColorDrawable(actionBarColor, outlineEnabled);
        return applyActionBarDrawable(context, d, isTransparentBackground(themeRes));
    }

    public static Context getActionBarContext(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.actionBarTheme,
                R.attr.actionBarWidgetTheme, android.R.attr.actionBarTheme, android.R.attr.actionBarWidgetTheme });
        final int resId;
        if (a.hasValue(0) || a.hasValue(1)) {
            resId = a.hasValue(0) ? a.getResourceId(0, 0) : a.getResourceId(1, 0);
        } else {
            resId = a.hasValue(2) ? a.getResourceId(2, 0) : a.getResourceId(3, 0);
        }
        a.recycle();
        if (resId == 0)
            return context;
        return new ContextThemeWrapper(context, resId);
    }

    public static Drawable getActionBarSplitBackground(final Context context, final int themeRes) {
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { android.R.attr.backgroundSplit },
                android.R.attr.actionBarStyle, themeRes);
        final Drawable d = a.getDrawable(0);
        a.recycle();
        return applyActionBarDrawable(context, d, isTransparentBackground(themeRes));
    }

    public static int getActionIconColor(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Light:
        case R.style.Theme_Firetweet_Light_SolidBackground:
        case R.style.Theme_Firetweet_Light_Transparent:
        case R.style.Theme_Firetweet_Light_Compose:
        case R.style.Theme_Firetweet_Light_Dialog:
        case R.style.Theme_Firetweet_ActionBar_Colored_Light:
        case R.style.Theme_Firetweet_Settings_Light:
        case R.style.Theme_Firetweet_Drawer_Light:
        case R.style.Theme_Firetweet_Drawer_Light_Transparent:
        case R.style.Theme_Firetweet_Settings_Light_DarkActionBar_DarkIcon:
            return 0x99333333;
        }
        return 0xCCFFFFFF;
    }

    public static int getColorBackgroundCacheHint(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackgroundCacheHint });
        final int color = a.getColor(0, Color.TRANSPARENT);
        a.recycle();
        return color;
    }

    public static int getCardBackgroundColor(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.cardItemBackgroundColor });
        final int color = a.getColor(0, Color.TRANSPARENT);
        a.recycle();
        final int themeAlpha = getThemeAlpha(context);
        return themeAlpha << 24 | (0x00FFFFFF & color);
    }

    public static int getComposeThemeResource(final Context context) {
        return getComposeThemeResource(getThemeNameOption(context), getDarkActionBarOption(context));
    }

    public static int getComposeThemeResource(final String name, final boolean darkActionBar) {
        if (VALUE_THEME_NAME_DARK.equals(name))
            return R.style.Theme_Firetweet_Dark_Compose;
        return R.style.Theme_Firetweet_Light_Compose;
    }

    public static boolean getDarkActionBarOption(final Context context) {
        if (context == null)
            return true;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        return pref.getBoolean(KEY_THEME_DARK_ACTIONBAR, true);
    }

    public static Context getDialogThemedContext(final Context context) {
        return new ContextThemeWrapper(context, getDialogThemeResource(context));
    }

    public static int getDialogThemeResource(final Context context) {
        return getDialogThemeResource(getThemeNameOption(context));
    }

    public static int getDialogThemeResource(final String name) {
        if (VALUE_THEME_NAME_DARK.equals(name))
            return R.style.Theme_Firetweet_Dark_Dialog;
        return R.style.Theme_Firetweet_Light_Dialog;
    }

    public static int getDrawerThemeResource(final Context context) {
        return getDrawerThemeResource(getThemeResource(context));
    }

    public static int getDrawerThemeResource(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Dark_Transparent:
        case R.style.Theme_Firetweet_Light_Transparent:
            return R.style.Theme_Firetweet_Drawer_Dark_Transparent;
        }
        return R.style.Theme_Firetweet_Drawer_Dark;
    }

    public static int getLightDrawerThemeResource(final Context context) {
        return getLightDrawerThemeResource(getThemeResource(context));
    }

    public static int getLightDrawerThemeResource(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Light_Transparent:
            return R.style.Theme_Firetweet_Drawer_Light_Transparent;
        }
        return R.style.Theme_Firetweet_Drawer_Light;
    }

    public static Drawable getImageHighlightDrawable(final Context context) {
        final Drawable d = getSelectableItemBackgroundDrawable(context);
        if (d != null) {
            d.setAlpha(0x80);
        }
        return d;
    }

    public static int getMenuIconColor(final Context context) {
        return getMenuIconColor(getThemeResource(context));
    }

    public static int getMenuIconColor(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Light:
        case R.style.Theme_Firetweet_Light_SolidBackground:
        case R.style.Theme_Firetweet_Light_Transparent:
        case R.style.Theme_Firetweet_Light_Compose:
        case R.style.Theme_Firetweet_Light_Dialog:
        case R.style.Theme_Firetweet_ActionBar_Colored_Light:
        case R.style.Theme_Firetweet_Settings_Light:
        case R.style.Theme_Firetweet_Settings_Light_DarkActionBar_DarkIcon:
            return 0x99333333;
        }
        return 0xCCFFFFFF;
    }

    public static int getNoDisplayThemeResource(final Context context) {
        if (context == null)
            return R.style.Theme_Firetweet_Dark_NoDisplay;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        final String theme = pref.getString(KEY_THEME, VALUE_THEME_NAME_TWIDERE);
        if (VALUE_THEME_NAME_DARK.equals(theme))
            return R.style.Theme_Firetweet_Dark_NoDisplay;
        return R.style.Theme_Firetweet_Light_NoDisplay;
    }

    public static Resources getResources(final Context context) {
        if (context instanceof IThemedActivity) {
            return ((IThemedActivity) context).getDefaultResources();
        }
        return context.getResources();
    }

    public static Drawable getSelectableItemBackgroundDrawable(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.selectableItemBackground });
        try {
            return a.getDrawable(0);
        } finally {
            a.recycle();
        }
    }

    public static int getSettingsThemeResource(final Context context) {
        return getSettingsThemeResource(getThemeNameOption(context), getDarkActionBarOption(context));
    }

    public static int getSettingsThemeResource(final String name, final boolean darkActionBar) {
        if (VALUE_THEME_NAME_TWIDERE.equals(name) || VALUE_THEME_NAME_LIGHT.equals(name))
            return darkActionBar ? R.style.Theme_Firetweet_Settings_Light_DarkActionBar
                    : R.style.Theme_Firetweet_Settings_Light;
        else if (VALUE_THEME_NAME_DARK.equals(name))
            return R.style.Theme_Firetweet_Settings_Dark;
        return R.style.Theme_Firetweet_Settings_Light_DarkActionBar;
    }

    public static int getSettingsWizardThemeResource(final Context context) {
        return R.style.Theme_Firetweet_Settings_Light;
    }

    public static int getTextAppearanceLarge(final Context context) {
        // final Context wrapped = getThemedContext(context,
        // getResources(context));
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.textAppearanceLarge });
        final int textAppearance = a.getResourceId(0, android.R.style.TextAppearance_Holo_Large);
        a.recycle();
        return textAppearance;
    }

    public static int getTextColorPrimary(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.textColorPrimary });
        try {
            return a.getColor(0, Color.TRANSPARENT);
        } finally {
            a.recycle();
        }
    }

    public static int getTextColorSecondary(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.textColorSecondary });
        try {
            return a.getColor(0, Color.TRANSPARENT);
        } finally {
            a.recycle();
        }
    }

    public static int getThemeAlpha(final Context context) {
        return getThemeAlpha(getThemeResource(context));
    }

    public static int getThemeAlpha(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Dark_Transparent:
        case R.style.Theme_Firetweet_Light_Transparent:
            return 0xa0;
        }
        return 0xff;
    }

    public static String getThemeBackgroundOption(final Context context) {
        if (context == null)
            return VALUE_THEME_BACKGROUND_DEFAULT;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        return pref.getString(KEY_THEME_BACKGROUND, VALUE_THEME_BACKGROUND_DEFAULT);
    }

    public static int getThemeColor(final Context context) {
        final Resources res = getResources(context);
        final Context wrapped = getThemedContext(context, res);
        final TypedArray a = wrapped.obtainStyledAttributes(new int[] { android.R.attr.colorActivatedHighlight });
        try {
            return a.getColor(0, res.getColor(R.color.material_light_blue));
        } finally {
            a.recycle();
        }
    }

    public static Context getThemedContext(final Context context) {
        return context;
    }

    public static Context getThemedContext(final Context context, final Resources res) {
        return context;
    }

    public static String getThemeFontFamily(final Context context) {
        if (context == null)
            return VALUE_THEME_FONT_FAMILY_REGULAR;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        final String fontFamily = pref.getString(KEY_THEME_FONT_FAMILY, VALUE_THEME_FONT_FAMILY_REGULAR);
        if (!TextUtils.isEmpty(fontFamily))
            return fontFamily;
        return VALUE_THEME_FONT_FAMILY_REGULAR;
    }

    public static int getThemeBackgroundColor(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackground });
        try {
            return a.getColor(0, 0);
        } finally {
            a.recycle();
        }
    }

    public static int getThemeBackgroundColor(final Context context, int themeRes) {
        if (themeRes == 0) {
            return getThemeBackgroundColor(context);
        }
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { android.R.attr.colorBackground }, 0,
                themeRes);
        try {
            return a.getColor(0, 0);
        } finally {
            a.recycle();
        }
    }

    public static int getThemeForegroundColor(final Context context) {
        return getThemeForegroundColor(context, 0);
    }

    public static int getThemeForegroundColor(final Context context, int theme) {
        final Context wrapped = theme != 0 ? new ContextThemeWrapper(context, theme) : context;
        final TypedArray a = wrapped.obtainStyledAttributes(new int[] { android.R.attr.colorForeground });
        try {
            return a.getColor(0, 0);
        } finally {
            a.recycle();
        }
    }

    @NonNull
    private static SharedPreferencesWrapper getSharedPreferencesWrapper(Context context) {
        final Context appContext = context.getApplicationContext();
        return SharedPreferencesWrapper.getInstance(appContext, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
    }

    public static String getThemeNameOption(final Context context) {
        if (context == null)
            return VALUE_THEME_NAME_TWIDERE;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        return pref.getString(KEY_THEME, VALUE_THEME_NAME_TWIDERE);
    }

    public static int getThemeResource(final Context context) {
        return getThemeResource(getThemeNameOption(context), getThemeBackgroundOption(context),
                getDarkActionBarOption(context));
    }

    public static int getNoActionBarThemeResource(final Context context) {
        return getNoActionBarThemeResource(getThemeNameOption(context), getThemeBackgroundOption(context),
                getDarkActionBarOption(context));
    }

    public static int getNoActionBarThemeResource(final String name, final String background,
            final boolean darkActionBar) {
        if (VALUE_THEME_NAME_LIGHT.equals(name)) {
            if (VALUE_THEME_BACKGROUND_SOLID.equals(background))
                return R.style.Theme_Firetweet_Light_SolidBackground_NoActionBar;
            else if (VALUE_THEME_BACKGROUND_TRANSPARENT.equals(background))
                return R.style.Theme_Firetweet_Light_Transparent_NoActionBar;
            return R.style.Theme_Firetweet_Light_NoActionBar;
        } else if (VALUE_THEME_NAME_DARK.equals(name)) {
            if (VALUE_THEME_BACKGROUND_SOLID.equals(background))
                return R.style.Theme_Firetweet_Dark_SolidBackground_NoActionBar;
            else if (VALUE_THEME_BACKGROUND_TRANSPARENT.equals(background))
                return R.style.Theme_Firetweet_Dark_Transparent_NoActionBar;
            return R.style.Theme_Firetweet_Dark_NoActionBar;
        }
        return R.style.Theme_Firetweet_Light_NoActionBar;
    }

    public static int getThemeResource(final String name, final String background, final boolean darkActionBar) {
        if (VALUE_THEME_NAME_LIGHT.equals(name)) {
            if (VALUE_THEME_BACKGROUND_SOLID.equals(background))
                return R.style.Theme_Firetweet_Light_SolidBackground;
            else if (VALUE_THEME_BACKGROUND_TRANSPARENT.equals(background))
                return R.style.Theme_Firetweet_Light_Transparent;
            return R.style.Theme_Firetweet_Light;

        } else if (VALUE_THEME_NAME_DARK.equals(name)) {
            if (VALUE_THEME_BACKGROUND_SOLID.equals(background))
                return R.style.Theme_Firetweet_Dark_SolidBackground;
            else if (VALUE_THEME_BACKGROUND_TRANSPARENT.equals(background))
                return R.style.Theme_Firetweet_Dark_Transparent;
            return R.style.Theme_Firetweet_Dark;
        }
        return R.style.Theme_Firetweet_Light;
    }

    public static int getTitleTextAppearance(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { android.R.attr.titleTextStyle },
                android.R.attr.actionBarStyle, android.R.style.Widget_Holo_ActionBar);
        final int textAppearance = a.getResourceId(0, android.R.style.TextAppearance_Holo);
        a.recycle();
        return textAppearance;
    }

    public static int getUserHighlightColor(final Context context) {
        final int color = getUserLinkTextColor(context);
        final int red = Color.red(color), green = Color.green(color), blue = Color.blue(color);
        return Color.argb(0x66, red, green, blue);
    }

    public static int getUserLinkTextColor(final Context context) {
        if (context == null)
            return new TextPaint().linkColor;
        final int themeColor = getUserAccentColor(context);
        final float[] hsv = new float[3];
        Color.colorToHSV(themeColor, hsv);
        if (isDarkTheme(context)) {
            hsv[2] = MathUtils.clamp(hsv[2], 1, 0.5f);
        } else {
            hsv[2] = MathUtils.clamp(hsv[2], 0.1f, 0.75f);
        }
        return Color.HSVToColor(hsv);
    }

    public static int getUserThemeBackgroundAlpha(final Context context) {
        if (context == null)
            return DEFAULT_THEME_BACKGROUND_ALPHA;
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        return pref.getInt(KEY_THEME_BACKGROUND_ALPHA, DEFAULT_THEME_BACKGROUND_ALPHA);
    }

    public static int getUserAccentColor(final Context context) {
        if (context == null)
            return Color.TRANSPARENT;
        final Resources res = getResources(context);
        final SharedPreferencesWrapper pref = getSharedPreferencesWrapper(context);
        final int def = res.getColor(R.color.branding_color);
        return pref.getInt(KEY_THEME_COLOR, def);
    }

    public static Typeface getUserTypeface(final Context context, final Typeface defTypeface) {
        if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
            return Typeface.DEFAULT;
        final int fontStyle = defTypeface != null ? defTypeface.getStyle() : Typeface.NORMAL;
        final String fontFamily = getThemeFontFamily(context);
        final Typeface tf = Typeface.create(fontFamily, fontStyle);
        if (tf != null)
            return tf;
        return Typeface.create(Typeface.DEFAULT, fontStyle);
    }

    public static int getViewerThemeResource(final Context context) {
        return R.style.Theme_Firetweet_Viewer;
    }

    public static Drawable getWindowBackground(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.windowBackground });
        final Drawable d = a.getDrawable(0);
        a.recycle();
        if (isTransparentBackground(context)) {
            applyThemeBackgroundAlphaToDrawable(context, d);
        }
        return d;
    }

    public static Drawable getWindowBackground(final Context context, final int themeRes) {
        final TypedArray a = context.obtainStyledAttributes(null, new int[] { android.R.attr.windowBackground }, 0,
                themeRes);
        final Drawable d = a.getDrawable(0);
        a.recycle();
        if (isTransparentBackground(themeRes)) {
            applyThemeBackgroundAlphaToDrawable(context, d);
        }
        return d;
    }

    public static Drawable getWindowContentOverlay(final Context context) {
        final TypedArray a = context.obtainStyledAttributes(new int[] { android.R.attr.windowContentOverlay });
        final Drawable d = a.getDrawable(0);
        a.recycle();
        return d;
    }

    public static boolean isDarkTheme(final Context context) {
        return isDarkTheme(getThemeResource(context));
    }

    public static boolean isDarkTheme(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Dark:
        case R.style.Theme_Firetweet_Dark_SolidBackground:
        case R.style.Theme_Firetweet_Dark_Dialog:
        case R.style.Theme_Firetweet_Dark_Compose:
        case R.style.Theme_Firetweet_Dark_Transparent:
        case R.style.Theme_Firetweet_Dark_NoActionBar:
        case R.style.Theme_Firetweet_Dark_SolidBackground_NoActionBar:
        case R.style.Theme_Firetweet_Dark_Transparent_NoActionBar:
            return true;
        }
        return false;
    }

    public static boolean isTransparentBackground(final Context context) {
        return isTransparentBackground(getThemeResource(context));
    }

    public static boolean isTransparentBackground(final int themeRes) {
        switch (themeRes) {
        case R.style.Theme_Firetweet_Dark_Transparent:
        case R.style.Theme_Firetweet_Light_Transparent:
        case R.style.Theme_Firetweet_Dark_Transparent_NoActionBar:
        case R.style.Theme_Firetweet_Light_Transparent_NoActionBar:
            return true;
        }
        return false;
    }

    public static void overrideActivityCloseAnimation(final Activity activity) {
        TypedArray a = activity.obtainStyledAttributes(new int[] { android.R.attr.windowAnimationStyle });
        final int windowAnimationStyleResId = a.getResourceId(0, 0);
        a.recycle();
        // Now retrieve the resource ids of the actual animations used in the
        // animation style pointed to by
        // the window animation resource id.
        a = activity.obtainStyledAttributes(windowAnimationStyleResId, ANIM_CLOSE_STYLE_ATTRS);
        final int activityCloseEnterAnimation = a.getResourceId(0, 0);
        final int activityCloseExitAnimation = a.getResourceId(1, 0);
        a.recycle();
        activity.overridePendingTransition(activityCloseEnterAnimation, activityCloseExitAnimation);
    }

    public static void overrideActivityOpenAnimation(final Activity activity) {

        TypedArray a = activity.obtainStyledAttributes(new int[] { android.R.attr.windowAnimationStyle });
        final int windowAnimationStyleResId = a.getResourceId(0, 0);
        a.recycle();
        // Now retrieve the resource ids of the actual animations used in the
        // animation style pointed to by
        // the window animation resource id.
        a = activity.obtainStyledAttributes(windowAnimationStyleResId, ANIM_OPEN_STYLE_ATTRS);
        final int activityOpenEnterAnimation = a.getResourceId(0, 0);
        final int activityOpenExitAnimation = a.getResourceId(1, 0);
        a.recycle();
        activity.overridePendingTransition(activityOpenEnterAnimation, activityOpenExitAnimation);
    }

    public static void overrideNormalActivityCloseAnimation(final Activity activity) {
        final TypedArray a = activity.obtainStyledAttributes(null, ANIM_CLOSE_STYLE_ATTRS, 0,
                android.R.style.Animation_Activity);
        final int activityCloseEnterAnimation = a.getResourceId(0, 0);
        final int activityCloseExitAnimation = a.getResourceId(1, 0);
        a.recycle();
        activity.overridePendingTransition(activityCloseEnterAnimation, activityCloseExitAnimation);
    }

    private static Drawable applyActionBarDrawable(final Context context, final Drawable d,
            final boolean applyAlpha) {
        if (d == null)
            return null;
        d.mutate();
        //        DrawableCompat.setTint(d, getUserAccentColor(context));
        if (applyAlpha) {
            d.setAlpha(getThemeAlpha(context));
        }
        return d;
    }

    private static View newViewInstance(final String className, final Context context, final AttributeSet attrs)
            throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException,
            InvocationTargetException {
        final Class<?> viewCls = Class.forName(className);
        final Constructor<?> constructor = viewCls.getConstructor(Context.class, AttributeSet.class);
        return (View) constructor.newInstance(context, attrs);
    }

    public static int getThemeColor(Context context, int themeResourceId) {
        final Context appContext = context.getApplicationContext();
        final Resources res = appContext.getResources();
        final TypedArray a = appContext.obtainStyledAttributes(null,
                new int[] { android.R.attr.colorActivatedHighlight }, 0, themeResourceId);
        try {
            return a.getColor(0, res.getColor(R.color.material_light_blue));
        } finally {
            a.recycle();
        }
    }

    public static boolean isColoredActionBar(int themeRes) {
        return !isDarkTheme(themeRes);
    }

    public static void initTextView(TextView view) {
        if (view.isInEditMode())
            return;
        final Context context = view.getContext();
        //        view.setLinkTextColor(ThemeUtils.getUserLinkTextColor(context));
        //        view.setHighlightColor(ThemeUtils.getUserHighlightColor(context));
        view.setTypeface(ThemeUtils.getUserTypeface(context, view.getTypeface()));
    }

    public static void applyParagraphSpacing(TextView textView, float multiplier) {
        final SpannableStringBuilder builder = SpannableStringBuilder.valueOf(textView.getText());
        int prevLineBreak, currLineBreak = 0;
        for (int i = 0, j = builder.length(); i < j; i++) {
            if (builder.charAt(i) == '\n') {
                prevLineBreak = currLineBreak;
                currLineBreak = i;
                if (currLineBreak > 0) {
                    builder.setSpan(new ParagraphSpacingSpan(multiplier), prevLineBreak, currLineBreak,
                            Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                }
            }
        }
        textView.setText(builder);
    }

}