org.appcelerator.titanium.util.TiUIHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.appcelerator.titanium.util.TiUIHelper.java

Source

/**
 * Appcelerator Titanium Mobile
 * Copyright (c) 2009-2012 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */
package org.appcelerator.titanium.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.digest.DigestUtils;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.common.CurrentActivityListener;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.kroll.common.TiMessenger;
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiBaseActivity;
import org.appcelerator.titanium.TiBlob;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.TiDimension;
import org.appcelerator.titanium.bitmappool.TiBitmapPool;
import org.appcelerator.titanium.io.TiBaseFile;
import org.appcelerator.titanium.io.TiFileFactory;
import org.appcelerator.titanium.proxy.ParentingProxy;
import org.appcelerator.titanium.proxy.TiViewProxy;
import org.appcelerator.titanium.proxy.TiWindowProxy;
import org.appcelerator.titanium.proxy.TiWindowProxy.PostOpenListener;
import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException;
import org.appcelerator.titanium.view.TiBackgroundDrawable;
import org.appcelerator.titanium.view.TiCompositeLayout.LayoutParams;
import org.appcelerator.titanium.view.TiDrawableReference;
import org.appcelerator.titanium.view.TiGradientDrawable;
import org.appcelerator.titanium.view.TiUIView;

import com.squareup.picasso.Cache;
import com.squareup.picasso.MarkableInputStream;
import com.trevorpage.tpsvg.SVGDrawable;
import com.trevorpage.tpsvg.SVGFlyweightFactory;
import com.udojava.evalex.Expression;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;

/**
 * A set of utility methods focused on UI and View operations.
 */
@SuppressWarnings("deprecation")
public class TiUIHelper {
    private static final String TAG = "TiUIHelper";
    private static final String customFontPath = "Resources/fonts";

    public static final int PORTRAIT = 1;
    public static final int UPSIDE_PORTRAIT = 2;
    public static final int LANDSCAPE_LEFT = 3;
    public static final int LANDSCAPE_RIGHT = 4;
    public static final int FACE_UP = 5;
    public static final int FACE_DOWN = 6;
    public static final int UNKNOWN = 7;
    public static final Pattern SIZED_VALUE = Pattern
            .compile("(-?[0-9]*\\.?[0-9]+)\\W*(px|dp|dip|sp|sip|mm|cm|pt|in)?");
    public static final String MIME_TYPE_PNG = "image/png";

    private static Method overridePendingTransition;
    private static Map<String, String> resourceImageKeys = Collections
            .synchronizedMap(new HashMap<String, String>());
    private static Map<String, Typeface> mCustomTypeFaces = Collections
            .synchronizedMap(new HashMap<String, Typeface>());
    private static String[] mCustomTypeFacesList;

    public static class FontDesc {
        public Float size = 15.0f;
        public int sizeUnit = TypedValue.COMPLEX_UNIT_PX;
        public Typeface typeface = null;
        public int style = Typeface.NORMAL;

        public void setDefaults(final Context context) {
            typeface = toTypeface(context, null);
            float[] result = new float[2];
            getSizeAndUnits(null, result);
            sizeUnit = (int) result[0];
            size = result[1];
        }
    }

    public static class Shadow {
        public float radius = 3;
        public float dx = 0;
        public float dy = 0;
        public int color = Color.BLACK;

        public Shadow() {
        }

        public Shadow(int color) {
            this.color = color;
        }
    }

    public static OnClickListener createDoNothingListener() {
        return new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // Do nothing
            }
        };
    }

    public static OnClickListener createKillListener() {
        return new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                Process.killProcess(Process.myPid());
            }
        };
    }

    public static OnClickListener createFinishListener(final Activity me) {
        return new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                me.finish();
            }
        };
    }

    public static void doKillOrContinueDialog(Context context, String title, String message,
            OnClickListener positiveListener, OnClickListener negativeListener) {
        if (positiveListener == null) {
            positiveListener = createDoNothingListener();
        }
        if (negativeListener == null) {
            negativeListener = createKillListener();
        }

        new AlertDialog.Builder(context).setTitle(title).setMessage(message)
                .setPositiveButton("Continue", positiveListener).setNegativeButton("Kill", negativeListener)
                .setCancelable(false).create().show();
    }

    public static void linkifyIfEnabled(final TextView tv, final Object autoLink) {
        if (autoLink != null) {
            //Default to Ti.UI.AUTOLINK_NONE
            boolean success = Linkify.addLinks(tv, TiConvert.toInt(autoLink, 0) & Linkify.ALL);
            if (success && tv.getText() instanceof Spanned) {
                tv.setMovementMethod(LinkMovementMethod.getInstance());
            }
        }
    }

    /**
     * Waits for the current activity to be ready, then invokes
     * {@link CurrentActivityListener#onCurrentActivityReady(Activity)}.
     * @param l the CurrentActivityListener.
     */
    public static void waitForCurrentActivity(final CurrentActivityListener l) {
        // Some window opens are async, so we need to make sure we don't
        // sandwich ourselves in between windows when transitioning
        // between activities TIMOB-3644
        TiWindowProxy waitingForOpen = TiWindowProxy.getWaitingForOpen();
        if (waitingForOpen != null) {
            waitingForOpen.setPostOpenListener(new PostOpenListener() {
                // TODO @Override
                public void onPostOpen(TiWindowProxy window) {
                    TiApplication app = TiApplication.getInstance();
                    Activity activity = app.getCurrentActivity();
                    if (activity != null) {
                        l.onCurrentActivityReady(activity);
                    }
                }
            });
        } else {
            TiApplication app = TiApplication.getInstance();
            Activity activity = app.getCurrentActivity();
            if (activity != null) {
                l.onCurrentActivityReady(activity);
            }
        }
    }

    /**
     * Creates and shows a dialog with an OK button given title and message.
     * The dialog's creation context is the current activity.
     * @param title  the title of dialog.
     * @param message  the dialog's message.
     * @param listener the click listener for click events.
     */
    public static void doOkDialog(final String title, final String message, OnClickListener listener) {
        if (listener == null) {
            listener = new OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Activity ownerActivity = ((AlertDialog) dialog).getOwnerActivity();
                    //if activity is not finishing, remove dialog to free memory
                    if (ownerActivity != null && !ownerActivity.isFinishing()) {
                        ((TiBaseActivity) ownerActivity).removeDialog((AlertDialog) dialog);
                    }
                }
            };
        }
        final OnClickListener fListener = listener;
        waitForCurrentActivity(new CurrentActivityListener() {
            // TODO @Override
            public void onCurrentActivityReady(final Activity activity) {
                //add dialog to activity for cleaning up purposes
                if (!activity.isFinishing()) {
                    AlertDialog dialog = new AlertDialog.Builder(activity).setTitle(title).setMessage(message)
                            .setPositiveButton(android.R.string.ok, fListener).setCancelable(false).create();
                    if (activity instanceof TiBaseActivity) {
                        TiBaseActivity baseActivity = (TiBaseActivity) activity;
                        baseActivity.addDialog(baseActivity.new DialogWrapper(dialog, true,
                                new WeakReference<TiBaseActivity>(baseActivity)));
                        dialog.setOwnerActivity(activity);
                    }
                    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
                        @Override
                        public void onShow(DialogInterface dialog) {
                            TiApplication.getInstance().cancelPauseEvent();
                        }
                    });
                    dialog.show();

                }

            }
        });
    }

    public static int toTypefaceStyle(final String fontWeight, final String fontStyle) {
        int style = Typeface.NORMAL;

        if (fontWeight != null) {
            if (fontWeight.equals("bold")) {
                if (fontStyle != null && fontStyle.equals("italic")) {
                    style = Typeface.BOLD_ITALIC;
                } else {
                    style = Typeface.BOLD;
                }
            } else if (fontStyle != null && fontStyle.equals("italic")) {
                style = Typeface.ITALIC;
            }
        } else if (fontStyle != null && fontStyle.equals("italic")) {
            style = Typeface.ITALIC;
        }
        return style;
    }

    public static int getSizeUnits(final String size) {
        int units = TypedValue.COMPLEX_UNIT_PX;
        String unitString = null;

        if (size != null) {
            Matcher m = SIZED_VALUE.matcher(size.trim());
            if (m.matches()) {
                if (m.groupCount() == 2) {
                    unitString = m.group(2);
                }
            }
        }

        if (unitString == null) {
            unitString = TiApplication.getInstance().getDefaultUnit();
        }

        if (TiDimension.UNIT_PX.equals(unitString) || TiDimension.UNIT_SYSTEM.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_PX;
        } else if (TiDimension.UNIT_PT.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_PT;
        } else if (TiDimension.UNIT_DP.equals(unitString) || TiDimension.UNIT_DIP.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_DIP;
        } else if (TiDimension.UNIT_SP.equals(unitString) || TiDimension.UNIT_SIP.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_SP;
        } else if (TiDimension.UNIT_MM.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_MM;
        } else if (TiDimension.UNIT_CM.equals(unitString)) {
            units = TiDimension.COMPLEX_UNIT_CM;
        } else if (TiDimension.UNIT_IN.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_IN;
        } else {
            if (unitString != null) {
                Log.w(TAG, "Unknown unit: " + unitString, Log.DEBUG_MODE);
            }
        }

        return units;
    }

    public static void getSizeAndUnits(final String size, final float[] result) {
        int units = TypedValue.COMPLEX_UNIT_PX;
        float value = 15.0f;
        String unitString = null;

        if (size != null) {
            Matcher m = SIZED_VALUE.matcher(size.trim());
            if (m.matches()) {
                if (m.groupCount() == 2) {
                    unitString = m.group(2);
                    value = Float.parseFloat(m.group(1));
                }
            }
        }

        if (unitString == null) {
            unitString = TiApplication.getInstance().getDefaultUnit();
        }

        if (TiDimension.UNIT_PX.equals(unitString) || TiDimension.UNIT_SYSTEM.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_PX;
        } else if (TiDimension.UNIT_PT.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_PT;
        } else if (TiDimension.UNIT_DP.equals(unitString) || TiDimension.UNIT_DIP.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_DIP;
        } else if (TiDimension.UNIT_SP.equals(unitString) || TiDimension.UNIT_SIP.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_SP;
        } else if (TiDimension.UNIT_MM.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_MM;
        } else if (TiDimension.UNIT_CM.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_MM;
            value *= 10;
        } else if (TiDimension.UNIT_IN.equals(unitString)) {
            units = TypedValue.COMPLEX_UNIT_IN;
        } else {
            if (unitString != null) {
                Log.w(TAG, "Unknown unit: " + unitString, Log.DEBUG_MODE);
            }
        }
        result[0] = units;
        result[1] = value;
    }

    public static float getSize(final String size) {
        float value = 15.0f;
        if (size != null) {
            Matcher m = SIZED_VALUE.matcher(size.trim());
            if (m.matches()) {
                value = Float.parseFloat(m.group(1));
            }
        }

        return value;
    }

    public static float getRawSize(final int unit, final float size, final Context context) {
        Resources r;
        if (context != null) {
            r = context.getResources();
        } else {
            r = Resources.getSystem();
        }
        return TypedValue.applyDimension(unit, size, r.getDisplayMetrics());
    }

    public static float getRawSize(final int unit, final float size) {
        return getRawSize(unit, size, null);
    }

    public static float getRawDIPSize(final float size, final Context context) {
        return getRawSize(TypedValue.COMPLEX_UNIT_DIP, size, context);
    }

    public static float getRawSize(final String size, final Context context) {
        float[] result = new float[2];
        getSizeAndUnits(size, result);
        return getRawSize((int) result[0], result[1], context);
    }

    public static float getRawSize(final int size, final Context context) {
        float[] result = new float[2];
        getSizeAndUnits(null, result);
        return getRawSize((int) result[0], size, context);
    }

    public static float getInPixels(final String size) {

        return getRawSize(size, null);
    }

    public static float getInPixels(final String size, final Context context) {
        return getInPixels(size, 0.0f, context);
    }

    public static float getInPixels(final String size, final float defaultValue, final Context context) {
        if (size == null || size.length() == 0) {
            if (defaultValue > 0) {
                return getRawSize(TypedValue.COMPLEX_UNIT_DIP, defaultValue, context);
            }
            return 0;
        }
        return getRawSize(size, context);
    }

    public static float getInPixels(final HashMap dict, final String property, final float defaultValue,
            final Context context) {
        if (dict.containsKey(property)) {
            return getRawSize(TiConvert.toString(dict.get(property)), context);
        }
        if (defaultValue > 0) {
            return getRawSize(TypedValue.COMPLEX_UNIT_DIP, defaultValue, context);
        }
        return 0;
    }

    public static float getInPixels(final HashMap dict, final String property, Context context) {
        return getInPixels(dict, property, 0.0f, context);
    }

    public static float getInPixels(final HashMap dict, final String property) {
        return getInPixels(dict, property, null);
    }

    @Deprecated
    public static float getRawSizeOrZero(final HashMap dict, final String property) {
        return getInPixels(dict, property, null);
    }

    public static float getInPixels(final HashMap dict, final String property, final float defaultValue) {
        return getInPixels(dict, property, defaultValue, null);
    }

    public static float getInPixels(final Object value) {
        return getInPixels(TiConvert.toString(value), null);
    }

    @Deprecated
    public static float getRawSizeOrZero(final Object value) {
        return getInPixels(TiConvert.toString(value), null);
    }

    public static float getInPixels(final Object value, final float defaultValue) {
        return getInPixels(TiConvert.toString(value), defaultValue, null);
    }

    public static FontDesc getFontStyle(final Context context, final HashMap<String, Object> d) {
        FontDesc desc = new FontDesc();
        if (d == null) {
            desc.setDefaults(context);
            return desc;
        }
        String fontSize = null;
        if (d.containsKey("size")) {
            fontSize = TiConvert.toString(d, "size");
        }
        float[] result = new float[2];
        getSizeAndUnits(fontSize, result);
        desc.sizeUnit = (int) result[0];
        desc.size = result[1];

        String fontWeight = null;
        String fontStyle = null;
        if (d.containsKey("weight")) {
            fontWeight = TiConvert.toString(d, "weight");
        }
        if (d.containsKey("style")) {
            fontStyle = TiConvert.toString(d, "style");
        }
        desc.style = toTypefaceStyle(fontWeight, fontStyle);

        String fontFamily = null;
        if (d.containsKey("family")) {
            fontFamily = TiConvert.toString(d, "family");
        }
        if (fontWeight != null && desc.style == Typeface.NORMAL && fontWeight != "normal") {
            desc.typeface = toTypeface(context, fontFamily, fontWeight);
        } else {
            desc.typeface = toTypeface(context, fontFamily, null);
        }
        return desc;
    }

    public static void setPadding(final View view, final RectF padding) {
        view.setPadding((int) padding.left, (int) padding.top, (int) padding.right, (int) padding.bottom);
    }

    public static void styleText(final TextView tv, final HashMap<String, Object> d) {
        styleText(tv, getFontStyle(tv.getContext(), d));
    }

    public static void styleText(final TextView tv, final FontDesc desc) {
        tv.setTypeface(desc.typeface, desc.style);
        tv.setTextSize(desc.sizeUnit, desc.size);
    }

    public static void styleText(TextView tv, String fontFamily, String fontSize, String fontWeight) {
        styleText(tv, fontFamily, fontSize, fontWeight, null);
    }

    public static void styleText(TextView tv, String fontFamily, String fontSize, String fontWeight,
            String fontStyle) {
        Typeface tf = tv.getTypeface();
        tf = toTypeface(tv.getContext(), fontFamily);
        tv.setTypeface(tf, toTypefaceStyle(fontWeight, fontStyle));
        tv.setTextSize(getSizeUnits(fontSize), getSize(fontSize));
    }

    public static boolean isAndroidTypeface(String fontFamily) {
        if (fontFamily != null) {
            if ("monospace".equals(fontFamily)) {
                return true;
            } else if ("serif".equals(fontFamily)) {
                return true;
            } else if ("sans-serif".equals(fontFamily)) {
                return true;
            }
        }
        return false;
    }

    public static Typeface toTypeface(final Context context, String fontFamily, String weight) {
        Typeface tf = Typeface.SANS_SERIF; // default
        if (weight != null) {
            if (fontFamily == null && weight != "regular") {
                fontFamily = "sans-serif-" + weight.toLowerCase();
            } else {
                fontFamily += "-" + weight.toLowerCase();
            }
        }
        if (fontFamily != null) {
            if ("monospace".equals(fontFamily)) {
                tf = Typeface.MONOSPACE;
            } else if ("serif".equals(fontFamily)) {
                tf = Typeface.SERIF;
            } else if ("sans-serif".equals(fontFamily)) {
                tf = Typeface.SANS_SERIF;
            } else {
                Typeface loadedTf = null;
                if (context != null) {
                    try {
                        loadedTf = loadTypeface(context, fontFamily);

                    } catch (Exception e) {
                        loadedTf = null;
                        Log.e(TAG, "Unable to load font " + fontFamily + ": " + e.getMessage());
                    }
                }
                if (loadedTf == null) {
                    Log.w(TAG, "Unsupported font: '" + fontFamily
                            + "' supported fonts are 'monospace', 'serif', 'sans-serif'.", Log.DEBUG_MODE);
                } else {
                    tf = loadedTf;
                }
            }
        }
        return tf;
    }

    public static Typeface toTypeface(final Context context, String fontFamily) {
        return toTypeface(context, fontFamily, null);
    }

    public static Typeface toTypeface(final String fontFamily) {
        return toTypeface(null, fontFamily);
    }

    @SuppressLint("DefaultLocale")
    private static Typeface loadTypeface(final Context context, final String fontFamily) {
        if (context == null) {
            return null;
        }
        if (mCustomTypeFaces.containsKey(fontFamily)) {
            return mCustomTypeFaces.get(fontFamily);
        }
        if (mCustomTypeFacesList == null) {
            AssetManager mgr = context.getAssets();
            try {
                ArrayList<String> fileList = new ArrayList<String>(Arrays.asList(mgr.list(customFontPath)));
                Iterator it = fileList.iterator();
                while (it.hasNext()) {
                    String text = (String) it.next();
                    if (!text.endsWith(".ttf") && !text.endsWith(".otf")) {
                        it.remove();
                    }
                }
                mCustomTypeFacesList = fileList.toArray(new String[] {});
            } catch (Exception e) {
                Log.e(TAG, "Unable to load 'fonts' assets. Perhaps doesn't exist? " + e.getMessage());
            }
        }
        if (mCustomTypeFacesList != null) {
            Typeface tf = null;
            AssetManager mgr = context.getAssets();
            for (String f : mCustomTypeFacesList) {
                if (f.equals(fontFamily) || f.startsWith(fontFamily + ".") || f.startsWith(fontFamily + "-")) {
                    tf = Typeface.createFromAsset(mgr, customFontPath + "/" + f);
                    synchronized (mCustomTypeFaces) {
                        mCustomTypeFaces.put(fontFamily, tf);
                    }
                    return tf;
                }
            }
            tf = Typeface.create(fontFamily, Typeface.NORMAL);
            if (tf != null) {
                synchronized (mCustomTypeFaces) {
                    mCustomTypeFaces.put(fontFamily, tf);
                }
                return tf;
            }
        }

        mCustomTypeFaces.put(fontFamily, null);
        return null;
    }

    public static String getDefaultFontSize(final Context context) {
        String size = "15.0px";
        TextView tv = new TextView(context);
        if (tv != null) {
            size = String.valueOf(tv.getTextSize()) + "px";
            tv = null;
        }

        return size;
    }

    public static String getDefaultFontWeight(final Context context) {
        String style = "normal";
        TextView tv = new TextView(context);
        if (tv != null) {
            Typeface tf = tv.getTypeface();
            if (tf != null && tf.isBold()) {
                style = "bold";
            }
        }

        return style;
    }

    public static int getGravity(final String align, final boolean vertical) {
        if (align != null) {
            if ("left".equals(align)) {
                return Gravity.LEFT;
            } else if ("center".equals(align)) {
                return vertical ? Gravity.CENTER_VERTICAL : Gravity.CENTER_HORIZONTAL;
            } else if ("right".equals(align)) {
                return Gravity.RIGHT;
            } else if ("top".equals(align)) {
                return Gravity.TOP;
            } else if ("bottom".equals(align)) {
                return Gravity.BOTTOM;
            }
        }
        return Gravity.NO_GRAVITY;
    }

    public static void setAlignment(final TextView tv, final String textAlign, final String verticalAlign) {
        int gravity = Gravity.NO_GRAVITY;

        if (textAlign != null) {
            if ("left".equals(textAlign)) {
                gravity |= Gravity.LEFT;
            } else if ("center".equals(textAlign) || "middle".equals(textAlign)) {
                gravity |= Gravity.CENTER_HORIZONTAL;
            } else if ("right".equals(textAlign)) {
                gravity |= Gravity.RIGHT;
            } else {
                Log.w(TAG, "Unsupported horizontal alignment: " + textAlign);
            }
        } else {
            // Nothing has been set - let's set if something was set previously
            // You can do this with shortcut syntax - but long term maint of code is easier if it's explicit
            //         Log.w(TAG,
            //            "No alignment set - old horizontal align was: " + (tv.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK),
            //            Log.DEBUG_MODE);

            if ((tv.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.NO_GRAVITY) {
                // Something was set before - so let's use it
                gravity |= tv.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK;
            }
        }

        if (verticalAlign != null) {
            if ("top".equals(verticalAlign)) {
                gravity |= Gravity.TOP;
            } else if ("middle".equals(verticalAlign) || "center".equals(verticalAlign)) {
                gravity |= Gravity.CENTER_VERTICAL;
            } else if ("bottom".equals(verticalAlign)) {
                gravity |= Gravity.BOTTOM;
            } else {
                Log.w(TAG, "Unsupported vertical alignment: " + verticalAlign);
            }
        } else {
            // Nothing has been set - let's set if something was set previously
            // You can do this with shortcut syntax - but long term maint of code is easier if it's explicit
            //         Log.w(TAG, "No alignment set - old vertical align was: " + (tv.getGravity() & Gravity.VERTICAL_GRAVITY_MASK),
            //            Log.DEBUG_MODE);
            if ((tv.getGravity() & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.NO_GRAVITY) {
                // Something was set before - so let's use it
                gravity |= tv.getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
            }
        }

        tv.setGravity(gravity);
    }

    public static final int FONT_SIZE_POSITION = 0;
    public static final int FONT_FAMILY_POSITION = 1;
    public static final int FONT_WEIGHT_POSITION = 2;
    public static final int FONT_STYLE_POSITION = 3;

    public static String[] getFontProperties(final KrollDict fontProps) {
        boolean bFontSet = false;
        String[] fontProperties = new String[4];
        if (fontProps.containsKey(TiC.PROPERTY_FONT) && fontProps.get(TiC.PROPERTY_FONT) instanceof HashMap) {
            bFontSet = true;
            HashMap font = (HashMap) fontProps.get(TiC.PROPERTY_FONT);
            if (font.containsKey(TiC.PROPERTY_FONTSIZE)) {
                fontProperties[FONT_SIZE_POSITION] = TiConvert.toString(font, TiC.PROPERTY_FONTSIZE);
            }
            if (font.containsKey(TiC.PROPERTY_FONTFAMILY)) {
                fontProperties[FONT_FAMILY_POSITION] = TiConvert.toString(font, TiC.PROPERTY_FONTFAMILY);
            }
            if (font.containsKey(TiC.PROPERTY_FONTWEIGHT)) {
                fontProperties[FONT_WEIGHT_POSITION] = TiConvert.toString(font, TiC.PROPERTY_FONTWEIGHT);
            }
            if (font.containsKey(TiC.PROPERTY_FONTSTYLE)) {
                fontProperties[FONT_STYLE_POSITION] = TiConvert.toString(font, TiC.PROPERTY_FONTSTYLE);
            }
        } else {
            if (fontProps.containsKey(TiC.PROPERTY_FONT_FAMILY)) {
                bFontSet = true;
                fontProperties[FONT_FAMILY_POSITION] = TiConvert.toString(fontProps, TiC.PROPERTY_FONT_FAMILY);
            }
            if (fontProps.containsKey(TiC.PROPERTY_FONT_SIZE)) {
                bFontSet = true;
                fontProperties[FONT_SIZE_POSITION] = TiConvert.toString(fontProps, TiC.PROPERTY_FONT_SIZE);
            }
            if (fontProps.containsKey(TiC.PROPERTY_FONT_WEIGHT)) {
                bFontSet = true;
                fontProperties[FONT_WEIGHT_POSITION] = TiConvert.toString(fontProps, TiC.PROPERTY_FONT_WEIGHT);
            }
            if (fontProps.containsKey(TiC.PROPERTY_FONT_STYLE)) {
                bFontSet = true;
                fontProperties[FONT_STYLE_POSITION] = TiConvert.toString(fontProps, TiC.PROPERTY_FONT_STYLE);
            }
        }
        if (!bFontSet) {
            return null;
        }
        return fontProperties;
    }

    public static void setTextViewDIPPadding(final TextView textView, final int horizontalPadding,
            final int verticalPadding) {
        int rawHPadding = (int) getRawDIPSize(horizontalPadding, textView.getContext());
        int rawVPadding = (int) getRawDIPSize(verticalPadding, textView.getContext());
        textView.setPadding(rawHPadding, rawVPadding, rawHPadding, rawVPadding);
    }

    public static final int[] BACKGROUND_DEFAULT_STATE_1 = { android.R.attr.state_window_focused,
            android.R.attr.state_enabled };
    public static final int[] BACKGROUND_DEFAULT_STATE_2 = { android.R.attr.state_enabled };
    public static final int[] BACKGROUND_SELECTED_STATE = { android.R.attr.state_window_focused,
            android.R.attr.state_enabled, android.R.attr.state_pressed };

    public static final int[] BACKGROUND_CHECKED_STATE = { android.R.attr.state_window_focused,
            android.R.attr.state_enabled, android.R.attr.state_checked };
    public static final int[] BACKGROUND_FOCUSED_STATE = { android.R.attr.state_focused,
            android.R.attr.state_window_focused, android.R.attr.state_enabled };
    public static final int[] BACKGROUND_DISABLED_STATE = { -android.R.attr.state_enabled };

    public static ColorDrawable buildColorDrawable(final Object color) {
        if (color instanceof Integer) {
            return new ColorDrawable((Integer) color);
        } else if (color instanceof String) {
            ColorDrawable colorDrawable = null;
            if (color != null) {
                colorDrawable = buildColorDrawable(TiColorHelper.parseColor((String) color));
            }
            return colorDrawable;
        }
        return null;
    }

    private static String resolveImageUrl(final String path, final KrollProxy proxy) {
        return path.length() > 0 ? proxy.resolveUrl(null, path) : null;
    }

    public static Drawable buildImageDrawable(final Context context, final Object object, final boolean tileImage,
            final KrollProxy proxy) {

        if (object instanceof TiBlob) {

            switch (((TiBlob) object).getType()) {
            case TiBlob.TYPE_DRAWABLE:
                return buildImageDrawable(context, ((TiBlob) object).getDrawable(), tileImage, proxy);
            case TiBlob.TYPE_FILE:
            case TiBlob.TYPE_IMAGE:
                final String cacheKey = ((TiBlob) object).getCacheKey();
                Bitmap b = TiApplication.getImageMemoryCache().get(cacheKey);
                if (b == null) {
                    b = ((TiBlob) object).getImage();
                }
                return buildImageDrawable(context, b, tileImage, proxy);
            default:
                return null;
            }
        } else if (object instanceof String) {
            String url = (String) object;
            if (url != null) {
                url = resolveImageUrl(url, proxy);
            }
            Drawable imageDrawable = null;
            if (url != null) {
                Cache cache = TiApplication.getImageMemoryCache();
                Bitmap bitmap = cache.get(url);
                if (bitmap == null) {
                    imageDrawable = TiFileHelper.loadDrawable(url);
                    if (imageDrawable instanceof BitmapDrawable) {
                        bitmap = ((BitmapDrawable) imageDrawable).getBitmap();
                        cache.set(url, ((BitmapDrawable) imageDrawable).getBitmap());
                    }
                } else {
                    imageDrawable = new BitmapDrawable(proxy.getActivity().getResources(), bitmap);
                }

                if (tileImage) {
                    if (imageDrawable instanceof BitmapDrawable) {
                        BitmapDrawable tiledBackground = (BitmapDrawable) imageDrawable;
                        tiledBackground.setTileModeX(Shader.TileMode.REPEAT);
                        tiledBackground.setTileModeY(Shader.TileMode.REPEAT);
                    }
                }
            }
            return imageDrawable;
        } else if (object instanceof Drawable) {
            Drawable imageDrawable = (Drawable) object;
            if (tileImage && imageDrawable instanceof BitmapDrawable) {
                BitmapDrawable tiledBackground = (BitmapDrawable) imageDrawable;
                tiledBackground.setTileModeX(Shader.TileMode.REPEAT);
                tiledBackground.setTileModeY(Shader.TileMode.REPEAT);
            }
            return imageDrawable;
        } else if (object instanceof Bitmap) {
            BitmapDrawable imageDrawable = new BitmapDrawable(context.getResources(), (Bitmap) object);
            if (tileImage) {
                if (imageDrawable instanceof BitmapDrawable) {
                    BitmapDrawable tiledBackground = (BitmapDrawable) imageDrawable;
                    tiledBackground.setTileModeX(Shader.TileMode.REPEAT);
                    tiledBackground.setTileModeY(Shader.TileMode.REPEAT);
                    imageDrawable = tiledBackground;
                }
            }
            return imageDrawable;
        }
        return null;
    }

    public static TiGradientDrawable buildGradientDrawable(final KrollDict gradientProperties) {
        TiGradientDrawable gradientDrawable = null;
        if (gradientProperties != null) {
            try {
                gradientDrawable = new TiGradientDrawable(gradientProperties);
            } catch (IllegalArgumentException e) {
                gradientDrawable = null;
            }
        }
        return gradientDrawable;
    }

    public static KrollDict createDictForImage(final int width, final int height, final byte[] data) {
        KrollDict d = new KrollDict();
        d.put(TiC.PROPERTY_X, 0);
        d.put(TiC.PROPERTY_Y, 0);
        d.put(TiC.PROPERTY_WIDTH, width);
        d.put(TiC.PROPERTY_HEIGHT, height);
        d.put(TiC.PROPERTY_MIMETYPE, MIME_TYPE_PNG);

        KrollDict cropRect = new KrollDict();
        cropRect.put(TiC.PROPERTY_X, 0);
        cropRect.put(TiC.PROPERTY_X, 0);
        cropRect.put(TiC.PROPERTY_WIDTH, width);
        cropRect.put(TiC.PROPERTY_HEIGHT, height);
        d.put(TiC.PROPERTY_CROP_RECT, cropRect);
        d.put(TiC.PROPERTY_MEDIA, TiBlob.blobFromObject(data, MIME_TYPE_PNG));

        return d;
    }

    public static TiBlob getImageFromDict(final KrollDict dict) {
        if (dict != null) {
            if (dict.containsKey(TiC.PROPERTY_MEDIA)) {
                Object media = dict.get(TiC.PROPERTY_MEDIA);
                if (media instanceof TiBlob) {
                    return (TiBlob) media;
                }
            }
        }
        return null;
    }

    /**
     * Draw the view into a bitmap.
     */
    public static Bitmap getViewBitmap(final View v) {
        v.clearFocus();
        v.setPressed(false);

        boolean willNotCache = v.willNotCacheDrawing();

        // Reset the drawing cache background color to fully transparent
        // for the duration of this operation
        int color = v.getDrawingCacheBackgroundColor();

        if (willNotCache || color != 0) {
            v.setWillNotCacheDrawing(false);
            v.setDrawingCacheBackgroundColor(0);
            v.destroyDrawingCache();
        }

        boolean isDrawinCacheEnabled = v.isDrawingCacheEnabled();

        Bitmap bitmap = null;

        try {
            v.setDrawingCacheEnabled(true);
            v.buildDrawingCache(true);
            bitmap = Bitmap.createBitmap(v.getDrawingCache(true));
        } catch (Exception e) {
            bitmap = null;
        }
        v.setDrawingCacheEnabled(isDrawinCacheEnabled);

        // Restore the view
        if (willNotCache || color != 0) {
            v.destroyDrawingCache();
            v.setDrawingCacheBackgroundColor(color);
            v.setWillNotCacheDrawing(willNotCache);
        }

        return bitmap;
    }

    public static Bitmap viewToBitmap(final LayoutParams layoutParams, final View view) {
        Bitmap bitmap = null;

        if (view != null) {
            int width = view.getWidth();
            int height = view.getHeight();
            if (view.getWidth() == 0 || view.getHeight() == 0) {
                // maybe move this out to a separate method once other refactor regarding "getWidth", etc is done
                if (view.getWidth() == 0 && layoutParams != null && layoutParams.optionWidth != null) {
                    width = layoutParams.optionWidth.getAsPixels(view);
                }
                if (view.getHeight() == 0 && layoutParams != null && layoutParams.optionHeight != null) {
                    height = layoutParams.optionHeight.getAsPixels(view);
                }

                int wmode = width == 0 ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
                int hmode = height == 0 ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
                view.measure(MeasureSpec.makeMeasureSpec(width, wmode), MeasureSpec.makeMeasureSpec(height, hmode));

                // Will force the view to layout itself, grab dimensions
                width = view.getMeasuredWidth();
                height = view.getMeasuredHeight();

                // set a default BS value if the dimension is still 0 and log a warning
                if (width == 0) {
                    width = 100;
                    Log.e(TAG, "Width property is 0 for view, display view before calling toImage()",
                            Log.DEBUG_MODE);
                }
                if (height == 0) {
                    height = 100;
                    Log.e(TAG, "Height property is 0 for view, display view before calling toImage()",
                            Log.DEBUG_MODE);
                }

            }

            if (view.getParent() == null) {
                Log.i(TAG, "View does not have parent, calling layout", Log.DEBUG_MODE);
                view.layout(0, 0, width, height);
            }
            bitmap = getViewBitmap(view);
            if (bitmap == null) {

                try {
                    bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
                    Canvas canvas = new Canvas(bitmap);
                    view.draw(canvas);
                    //                   canvas = null;
                } catch (Exception e) {
                    bitmap = null;
                }
            }
        }

        return bitmap;
    }

    //   public static TiBlob viewToImage(final KrollDict proxyDict, final View view)
    //   {
    //      Bitmap bitmap = viewToBitmap(proxyDict, view);
    //      if (bitmap != null) {
    //         return TiBlob.blobFromObject(bitmap);
    //      }
    //      return null;
    //   }

    /**
     * Creates and returns a Bitmap from an InputStream.
     * @param stream an InputStream to read bitmap data.
     * @param opts BitmapFactory options
     * @return a new bitmap instance.
     * @module.api
     */
    public static Bitmap createBitmap(InputStream stream, BitmapFactory.Options opts) {
        Rect pad = new Rect();
        if (opts == null) {
            opts = TiBitmapPool.defaultBitmapOptions();
        }
        Bitmap b = null;
        try {
            MarkableInputStream markStream = new MarkableInputStream(stream);
            stream = markStream;

            long mark = markStream.savePosition(65536); // TODO fix this crap.
            markStream.reset(mark);
            TiApplication.getBitmapOptionsTransformer().transformOptions(markStream, opts, mark);
            b = BitmapFactory.decodeResourceStream(null, null, stream, pad, opts);
        } catch (OutOfMemoryError e) {
            Log.e(TAG, "Unable to load bitmap. Not enough memory: " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return b;
    }

    /**
     * Creates and returns a Bitmap from an InputStream.
     * @param stream an InputStream to read bitmap data.
     * @return a new bitmap instance.
     * @module.api
     */
    public static Bitmap createBitmap(final InputStream stream) {
        return createBitmap(stream, null);
    }

    private static final Pattern drawablePattern = Pattern.compile("/Resources/(.*)\\.png$",
            Pattern.CASE_INSENSITIVE);
    private static final Pattern imagePattern = Pattern.compile("^.*/Resources/images/(.*$)");

    private static BitmapFactory.Options bmpOptions = null;

    private static BitmapFactory.Options getScaledBitmapOptions() {
        if (bmpOptions == null) {
            bmpOptions = new BitmapFactory.Options();
            bmpOptions.inPurgeable = true;
            bmpOptions.inInputShareable = true;
            bmpOptions.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            bmpOptions.inTargetDensity = TiApplication.getAppDensityDpi();
            bmpOptions.inScaled = true;
        }
        return bmpOptions;
    }

    /**
     * Creates and returns a density scaled Bitmap from an InputStream.
     * @param stream an InputStream to read bitmap data.
     * @return a new bitmap instance.
     */
    public static Bitmap createDensityScaledBitmap(final InputStream stream) {
        Bitmap b = null;
        try {
            b = BitmapFactory.decodeResourceStream(null, null, stream, null, getScaledBitmapOptions());
        } catch (OutOfMemoryError e) {
            Log.e(TAG, "Unable to load bitmap. Not enough memory: " + e.getMessage());
        }
        return b;
    }

    private static String getResourceKeyForImage(final String url) {
        if (resourceImageKeys.containsKey(url)) {
            return resourceImageKeys.get(url);
        }

        Matcher matcher = imagePattern.matcher(url);
        String chopped = null;
        if (!matcher.matches()) {
            matcher = drawablePattern.matcher(url);
            if (matcher.find()) {
                chopped = matcher.group(1);
            }
            if (chopped != null) {
                if (chopped.endsWith(".9")) {
                    chopped = chopped.substring(0, chopped.lastIndexOf(".9"));
                }
                resourceImageKeys.put(url, chopped);
            }
            return chopped;
        }

        chopped = matcher.group(1);
        if (chopped == null) {
            return null;
        }

        chopped = chopped.toLowerCase();
        String forHash = chopped;
        if (forHash.endsWith(".9.png")) {
            forHash = forHash.replace(".9.png", ".png");
        }
        String withoutExtension = chopped;

        if (chopped.matches("^.*\\..*$")) {
            if (chopped.endsWith(".9.png")) {
                withoutExtension = chopped.substring(0, chopped.lastIndexOf(".9.png"));
            } else {
                withoutExtension = chopped.substring(0, chopped.lastIndexOf('.'));
            }
        }

        String cleanedWithoutExtension = withoutExtension.replaceAll("[^a-z0-9_]", "_");
        StringBuilder result = new StringBuilder(100);
        result.append(cleanedWithoutExtension.substring(0, Math.min(cleanedWithoutExtension.length(), 80)));
        result.append("_");
        result.append(DigestUtils.md5Hex(forHash).substring(0, 10));
        String sResult = result.toString();
        resourceImageKeys.put(url, sResult);
        return sResult;
    }

    public static int getResourceId(final String url) {
        if (!url.contains("Resources/")) {
            return 0;
        }

        String key = getResourceKeyForImage(url);
        if (key == null) {
            return 0;
        }

        try {
            return TiRHelper.getResource("drawable." + key, false);
        } catch (TiRHelper.ResourceNotFoundException e) {
            return 0;
        }
    }

    public static int getResourceId(final Object value, final KrollProxy proxy) {
        if (value instanceof Number) {
            return ((Number) value).intValue();
        } else {
            String iconUrl = TiConvert.toString(value);
            if (iconUrl == null) {
                return 0;
            }
            String iconFullUrl = proxy.resolveUrl(null, iconUrl);
            return TiUIHelper.getResourceId(iconFullUrl);
        }
    }

    /**
     * Creates and returns a bitmap from its url.
     * @param url the bitmap url.
     * @return a new bitmap instance
     * @module.api
     */
    public static Bitmap getResourceBitmap(final String url) {
        int id = getResourceId(url);
        if (id == 0) {
            return null;
        } else {
            return getResourceBitmap(id);
        }
    }

    /**
     * Creates and returns a bitmap for the specified resource ID.
     * @param res_id the bitmap id.
     * @return a new bitmap instance.
     * @module.api
     */
    public static Bitmap getResourceBitmap(final int res_id) {
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inPurgeable = true;
        opts.inInputShareable = true;

        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeResource(TiApplication.getInstance().getResources(), res_id, opts);
        } catch (OutOfMemoryError e) {
            Log.e(TAG, "Unable to load bitmap. Not enough memory: " + e.getMessage());
        }
        return bitmap;
    }

    public static Drawable loadFastDevDrawable(final String url) {
        try {
            TiBaseFile tbf = TiFileFactory.createTitaniumFile(new String[] { url }, false);
            InputStream stream = tbf.getInputStream();
            Drawable d = BitmapDrawable.createFromStream(stream, url);
            stream.close();
            return d;
        } catch (IOException e) {
            Log.w(TAG, e.getMessage(), e);
        }
        return null;
    }

    public static Drawable getResourceDrawable(final String url) {
        int id = getResourceId(url);
        if (id == 0) {
            return null;
        }
        if (url.endsWith(".svg")) {
            return new SVGDrawable(
                    SVGFlyweightFactory.getInstance().get(id, TiApplication.getInstance().getCurrentActivity()));
        } else {
            return getResourceDrawable(id);
        }
    }

    public static Drawable getResourceDrawable(final int res_id) {
        return TiApplication.getInstance().getResources().getDrawable(res_id);
    }

    public static Drawable getResourceDrawable(final Object path) {
        Drawable d = null;

        try {

            if (path instanceof Number) {
                return getResourceDrawable(((Number) path).intValue());
            } else if (path instanceof String) {
                TiUrl imageUrl = new TiUrl((String) path);
                d = TiFileHelper.loadDrawable(imageUrl.resolve());
            } else {
                d = TiDrawableReference.fromObject(TiApplication.getInstance().getCurrentActivity(), path)
                        .getDrawable();
            }
        } catch (Exception e) {
            Log.w(TAG, "Could not load drawable " + e.getMessage(), Log.DEBUG_MODE);
            d = null;
        }
        return d;
    }

    public static void overridePendingTransition(final Activity activity) {
        if (overridePendingTransition == null) {
            try {
                overridePendingTransition = Activity.class.getMethod("overridePendingTransition", Integer.TYPE,
                        Integer.TYPE);
            } catch (NoSuchMethodException e) {
                Log.w(TAG, "Activity.overridePendingTransition() not found");
            }

        }

        if (overridePendingTransition != null) {
            try {
                overridePendingTransition.invoke(activity, new Object[] { 0, 0 });
            } catch (InvocationTargetException e) {
                Log.e(TAG, "Called incorrectly: " + e.getMessage());
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Illegal access: " + e.getMessage());
            }
        }
    }

    public static ColorFilter createColorFilterForOpacity(final float opacity) {
        // 5x4 identity color matrix + fade the alpha to achieve opacity
        float[] matrix = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, opacity, 0 };

        return new ColorMatrixColorFilter(new ColorMatrix(matrix));
    }

    public static void setDrawableOpacity(final Drawable drawable, final float opacity) {
        if (drawable instanceof ColorDrawable || drawable instanceof TiBackgroundDrawable) {
            drawable.setAlpha(Math.round(opacity * 255));
        } else if (drawable != null) {
            drawable.setColorFilter(createColorFilterForOpacity(opacity));
        }
    }

    public static void setPaintOpacity(final Paint paint, final float opacity) {
        paint.setColorFilter(createColorFilterForOpacity(opacity));
    }

    public static void requestSoftInputChange(final TiUIView uiView, final View view) {
        int focusState = uiView.getFocusState();
        if (focusState > TiUIView.SOFT_KEYBOARD_DEFAULT_ON_FOCUS) {
            if (focusState == TiUIView.SOFT_KEYBOARD_SHOW_ON_FOCUS) {
                showSoftKeyboard(view, true);
            } else if (focusState == TiUIView.SOFT_KEYBOARD_HIDE_ON_FOCUS) {
                showSoftKeyboard(view, false);
            } else {
                Log.w(TAG, "Unknown onFocus state: " + focusState);
            }
        }
    }

    /**
     * hides the soft keyboard.
     * @param view the current focused view.
     */
    public static void hideSoftKeyboard(final View view) {
        showSoftKeyboard(view, false);
    }

    /**
     * Shows/hides the soft keyboard.
     * @param view the current focused view.
     * @param show whether to show soft keyboard.
     */
    public static void showSoftKeyboard(final View view, final boolean show) {
        if (view == null)
            return;
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Activity.INPUT_METHOD_SERVICE);

        if (imm != null) {
            boolean useForce = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.DONUT || Build.VERSION.SDK_INT >= 8)
                    ? true
                    : false;
            String model = TiPlatformHelper.getInstance().getModel();
            if (model != null && model.toLowerCase().startsWith("droid")) {
                useForce = true;
            }
            if (show) {
                imm.showSoftInput(view,
                        useForce ? InputMethodManager.SHOW_FORCED : InputMethodManager.SHOW_IMPLICIT);
            } else {
                imm.hideSoftInputFromWindow(view.getWindowToken(),
                        useForce ? 0 : InputMethodManager.HIDE_IMPLICIT_ONLY);
            }
        }
    }

    /**
     * Run the Runnable "delayed" by using an AsyncTask to first require a new
     * thread and only then, in onPostExecute, run the Runnable on the UI thread.
     * @param runnable Runnable to run on UI thread.
     */
    public static void runUiDelayed(final Runnable runnable) {
        (new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... arg0) {
                return null;
            }

            /**
             * Always invoked on UI thread.
             */
            @Override
            protected void onPostExecute(Void result) {
                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(runnable);
            }
        }).execute();
    }

    /**
     * If there is a block on the UI message queue, run the Runnable "delayed".
     * @param runnable Runnable to run on UI thread.
     */
    public static void runUiDelayedIfBlock(final Runnable runnable) {
        //if (TiApplication.getInstance().getMessageQueue().isBlocking()) {
        if (TiMessenger.getMainMessenger().isBlocking()) {
            runUiDelayed(runnable);
        } else {
            //Handler handler = new Handler(Looper.getMainLooper());
            //handler.post(runnable);
            TiMessenger.getMainMessenger().getHandler().post(runnable);
        }
    }

    public static void firePostLayoutEvent(final TiUIView view) {
        if (view != null && view.getProxy() != null && !view.getProxy().getAnimating()) {
            view.getProxy().fireEvent(TiC.EVENT_POST_LAYOUT, null, false);
        }
    }

    public static boolean isViewInsideViewOfClass(final View view, final Class<?>[] testClass) {
        ViewParent parent = view.getParent();
        if (parent != null) {
            for (int i = 0; i < testClass.length; i++) {
                if (testClass[i].isAssignableFrom(parent.getClass()))
                    return true;
            }
            if (parent instanceof View)
                return isViewInsideViewOfClass((View) parent, testClass);
        }
        return false;
    }

    public static void removeViewFromSuperView(final View view) {
        if (view == null)
            return;
        ViewGroup parentViewGroup = (ViewGroup) view.getParent();
        if (parentViewGroup != null) {
            parentViewGroup.removeView(view);
        }
    }

    public static void removeViewFromSuperView(final TiViewProxy viewProxy) {
        ParentingProxy parentProxy = viewProxy.getParent();
        //Remove parent view if possible
        if (parentProxy != null && parentProxy instanceof TiViewProxy) {
            TiUIView tiView = viewProxy.peekView();
            TiUIView parentView = ((TiViewProxy) parentProxy).peekView();
            if (parentView != null && tiView != null) {
                parentView.remove(tiView);
            }
            viewProxy.setParent(null);
        }
    }

    public static void safeAddView(final ViewGroup parent, final View view) {
        if (parent == null || view == null)
            return;
        removeViewFromSuperView(view);
        parent.addView(view);
    }

    public static void addView(final ViewGroup parent, final View view) {
        removeViewFromSuperView(view);
        parent.addView(view);
    }

    public static void addView(final ViewGroup parent, final TiViewProxy viewProxy) {
        TiUIView tiView = viewProxy.getOrCreateView();
        if (tiView == null)
            return;
        View view = tiView.getOuterView();
        if (view == null)
            return;
        removeViewFromSuperView(view);
        parent.addView(view, tiView.getLayoutParams());
    }

    public static void addView(final ViewGroup parent, final View view, final int index) {
        removeViewFromSuperView(view);
        parent.addView(view, index);
    }

    public static void addView(final ViewGroup parent, final View view, final ViewGroup.LayoutParams params) {
        removeViewFromSuperView(view);
        parent.addView(view, params);
    }

    public static void addView(final ViewGroup parent, final View view, final int index,
            ViewGroup.LayoutParams params) {
        removeViewFromSuperView(view);
        parent.addView(view, index, params);
    }

    public static KrollDict getViewRectDict(final View view) {
        return TiConvert.toRectDict(view.getLeft(), view.getTop(), view.getWidth(), view.getHeight());
        //      TiDimension nativeWidth = new TiDimension(view.getWidth(), TiDimension.TYPE_WIDTH);
        //      TiDimension nativeHeight = new TiDimension(view.getHeight(), TiDimension.TYPE_HEIGHT);
        //      TiDimension nativeLeft = new TiDimension(view.getLeft(), TiDimension.TYPE_LEFT);
        //      TiDimension nativeTop = new TiDimension(view.getTop(), TiDimension.TYPE_TOP);
        //
        //        KrollDict d = new KrollDict();
        //      Activity activity  = TiApplication.getAppCurrentActivity();
        //        if (activity != null) {
        //            View decorView = activity.getWindow().getDecorView();
        //            d.put(TiC.PROPERTY_WIDTH, nativeWidth.getAsDefault(decorView));
        //            d.put(TiC.PROPERTY_HEIGHT, nativeHeight.getAsDefault(decorView));
        //            d.put(TiC.PROPERTY_X, nativeLeft.getAsDefault(decorView));
        //            d.put(TiC.PROPERTY_Y, nativeTop.getAsDefault(decorView));
        //        }
        //      return d;
    }

    public static RectF insetRect(final RectF source, final RectF inset) {
        if (inset == null)
            return source;
        return new RectF(source.left + inset.left, source.top + inset.top, source.right - inset.right,
                source.bottom - inset.bottom);
    }

    public static RectF insetRect(final RectF source, final float inset) {
        if (inset == 0.0)
            return source;
        return new RectF(source.left + inset, source.top + inset, source.right - inset, source.bottom - inset);
    }

    public static Shadow getShadow(final KrollDict dict) {
        Shadow result = new Shadow();
        if (dict == null)
            return result;
        if (dict.containsKey(TiC.PROPERTY_OFFSET)) {
            HashMap offset = (HashMap) dict.get(TiC.PROPERTY_OFFSET);
            result.dx = TiUIHelper.getInPixels(offset, TiC.PROPERTY_X);
            result.dy = TiUIHelper.getInPixels(offset, TiC.PROPERTY_Y);
        }
        result.radius = dict.optFloat(TiC.PROPERTY_RADIUS, 3);
        if (dict.containsKey(TiC.PROPERTY_COLOR)) {
            result.color = TiConvert.toColor(dict, TiC.PROPERTY_COLOR, result.color);
        }
        return result;
    }

    public static int adjustColorAlpha(final int color, final float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = Color.red(color);
        int green = Color.green(color);
        int blue = Color.blue(color);
        return Color.argb(alpha, red, green, blue);
    }

    private static Object getValueForKeyPath(final String key, final HashMap object) {
        Object current = object;
        Object result = null;
        String[] parts = TiUtils.fastSplit(key, '.');
        int length = parts.length;
        int canReturnIndex = length - 1;
        for (int i = 0; i < length; i++) {
            final String part = parts[i];
            if (current instanceof KrollProxy) {
                result = ((KrollProxy) current).getProperty(part);
                if (result == null) {
                    final String getter = "get" + Character.toUpperCase(part.charAt(0)) + part.substring(1);
                    Method method;
                    try {
                        method = current.getClass().getMethod(getter, (Class<?>[]) null);
                        result = method.invoke(current, (Object[]) null);
                    } catch (Exception e) {
                        result = null;
                    }
                }

            } else if (current instanceof HashMap) {
                result = ((HashMap) current).get(parts[i]);
            } else {
                result = null;
            }
            if (result != null) {
                current = result;
            } else {
                if (i != canReturnIndex) {
                    return null;
                }
                break;
            }
        }
        return result;
    }

    private static final char VAR_PREFIX = '_';

    static HashMap<String, Object> prepareExpressions(KrollProxy target, HashMap<String, Object> targetDict,
            KrollDict expressions, HashMap cashedValues) {
        HashMap<String, Object> props = targetDict.containsKey("properties")
                ? TiConvert.toHashMap(targetDict.get("properties"))
                : targetDict;
        KrollDict realProps = new KrollDict();
        if (cashedValues == null) {
            cashedValues = new HashMap();
        }
        for (Map.Entry<String, Object> entry : props.entrySet()) {
            final String key = entry.getKey();
            final Object theValue = entry.getValue();
            if (theValue instanceof HashMap) {
                Object binded = target.getProperty(key);
                if (binded instanceof KrollProxy) {
                    realProps.put(key, prepareExpressions((KrollProxy) binded, (HashMap<String, Object>) theValue,
                            expressions, cashedValues));
                }
            } else {
                final String value = TiConvert.toString(entry.getValue());
                Object current = ((KrollProxy) target).getProperty(key);
                if (current != null) {
                    expressions.put(VAR_PREFIX + "current", current);
                } else {
                    expressions.put(VAR_PREFIX + "current", 0);
                }
                if (cashedValues.containsKey(value)) {
                    realProps.put(key, cashedValues.get(value));
                } else {
                    Expression expression = new Expression(value);
                    for (Map.Entry<String, Object> entry2 : expressions.entrySet()) {
                        Object value2 = entry2.getValue();
                        if (value2 != null) {
                            expression.with(entry2.getKey(), TiConvert.toString(value2));
                        }
                    }
                    try {
                        String result = expression.eval().toPlainString();
                        cashedValues.put(value, result);
                        realProps.put(key, result);
                    } catch (Exception e) {
                        String result = new String(value);
                        for (Map.Entry<String, Object> entry2 : expressions.entrySet()) {
                            result = TiUtils.fastReplace(result, entry2.getKey(),
                                    TiConvert.toString(entry2.getValue()));
                        }
                        cashedValues.put(value, result);
                        realProps.put(key, result);
                    }
                }
            }
        }
        return realProps;
    }

    public static void applyMathDict(final KrollDict mathDict, final KrollDict event, final KrollProxy source) {
        if (event == null)
            return;
        KrollDict expressions = new KrollDict();

        HashMap<String, Object> vars = mathDict.getHashMap("variables");
        if (vars != null) {
            for (Map.Entry<String, Object> entry : vars.entrySet()) {
                Object value = entry.getValue();
                if (value instanceof String) {
                    expressions.put(VAR_PREFIX + entry.getKey(), getValueForKeyPath((String) value, event));
                } else {
                    expressions.put(VAR_PREFIX + entry.getKey(), value);
                }
            }
        }

        String condition = mathDict.getString("condition");
        if (condition != null) {
            try {
                Expression expression = new Expression(condition);
                for (Map.Entry<String, Object> entry2 : expressions.entrySet()) {
                    Object value = entry2.getValue();
                    if (value instanceof String) {
                        expression.with(entry2.getKey(), (String) value);
                    } else if (value instanceof Boolean) {
                        expression.with(entry2.getKey(), BigDecimal.valueOf((boolean) value ? 1 : 0));
                    } else if (value instanceof Integer || value instanceof Double) {
                        expression.with(entry2.getKey(), BigDecimal.valueOf((double) value));
                    }
                }
                BigDecimal result = expression.eval();
                if (result.intValue() == 0) {
                    return;
                }
            } catch (Exception e) {
                TiApplication.showExceptionError(e);
            }
        }

        HashMap<String, Object> exps = mathDict.getHashMap("expressions");
        if (exps != null) {
            for (Map.Entry<String, Object> entry : exps.entrySet()) {

                //               Scope scope = Scope.create();
                //               try {
                //                    for (Map.Entry<String, Object> entry2 : expressions.entrySet()) {
                //                        Variable v = scope.getVariable("$"+entry2.getKey());
                //                        v.setValue(TiConvert.toFloat(entry2.getValue()));
                //                    }
                //                    parsii.eval.Expression expr = Parser.parse(TiConvert.toString(entry.getValue()), scope);
                //                    expressions.put(entry.getKey(), expr.evaluate());
                //                    
                //                } catch (ParseException e1) {
                //                } 

                try {
                    Expression expression = new Expression(TiConvert.toString(entry.getValue()));
                    for (Map.Entry<String, Object> entry2 : expressions.entrySet()) {
                        String value = TiConvert.toString(entry2.getValue());
                        if (value != null) {
                            expression.with(entry2.getKey(), value);
                        }
                    }
                    expressions.put(VAR_PREFIX + entry.getKey(), expression.eval().toPlainString());
                } catch (Exception e) {
                    TiApplication.showExceptionError(e);
                    //                    return; 
                }
            }
        }

        Object[] targets = mathDict.getArray("targets");

        if (targets != null) {
            for (Object obj : targets) {
                HashMap<String, Object> targetDict = TiConvert.toHashMap(obj);
                if (targetDict != null) {
                    Object target = targetDict.get("target");
                    if (target == null) {
                        target = source;
                    } else if (target instanceof String) {
                        target = source.getProperty((String) target);
                    }
                    if (target instanceof KrollProxy) {
                        HashMap<String, Object> targetVariables = TiConvert
                                .toHashMap(targetDict.get("targetVariables"));
                        if (targetVariables != null) {
                            for (Map.Entry<String, Object> entry : targetVariables.entrySet()) {
                                final String key = entry.getKey();
                                Object current = ((KrollProxy) target)
                                        .getProperty(TiConvert.toString(entry.getValue()), true);
                                if (current != null) {
                                    expressions.put(VAR_PREFIX + key, current);
                                } else {
                                    expressions.put(VAR_PREFIX + key, 0);
                                }
                            }
                        }
                        HashMap realProps = prepareExpressions((KrollProxy) target, targetDict, expressions, null);
                        ((KrollProxy) target).applyPropertiesInternal(realProps, false, false);
                    }
                }
            }
        }
    }

    /**
     * To get the redirected Uri
     * @param Uri
     */
    public static Uri getRedirectUri(Uri mUri) throws MalformedURLException, IOException {
        if (!TiC.HONEYCOMB_OR_GREATER && ("http".equals(mUri.getScheme()) || "https".equals(mUri.getScheme()))) {
            // Media player doesn't handle redirects, try to follow them
            // here. (Redirects work fine without this in ICS.)
            while (true) {
                // java.net.URL doesn't handle rtsp
                if (mUri.getScheme() != null && mUri.getScheme().equals("rtsp"))
                    break;

                URL url = new URL(mUri.toString());
                HttpURLConnection cn = (HttpURLConnection) url.openConnection();
                cn.setInstanceFollowRedirects(false);
                String location = cn.getHeaderField("Location");
                if (location != null) {
                    String host = mUri.getHost();
                    int port = mUri.getPort();
                    String scheme = mUri.getScheme();
                    mUri = Uri.parse(location);
                    if (mUri.getScheme() == null) {
                        // Absolute URL on existing host/port/scheme
                        if (scheme == null) {
                            scheme = "http";
                        }
                        String authority = port == -1 ? host : host + ":" + port;
                        mUri = mUri.buildUpon().scheme(scheme).encodedAuthority(authority).build();
                    }
                } else {
                    break;
                }
            }
        }
        return mUri;
    }

    public static FragmentTransaction transactionFragment(final Fragment fragment, final View container,
            final FragmentActivity activity) {
        FragmentManager manager = activity.getSupportFragmentManager();
        Fragment tabFragment = manager.findFragmentById(android.R.id.tabcontent);
        // check if is opened inside an actionbar tab, which is
        // another fragment
        if (tabFragment != null) {
            manager = tabFragment.getChildFragmentManager();
        }
        FragmentTransaction transaction = null;
        transaction = manager.beginTransaction();
        transaction.add(container.getId(), fragment);
        transaction.commit();
        return transaction;
    }

    public static FragmentTransaction removeFragment(final Fragment fragment, final View container,
            final FragmentActivity activity) {
        FragmentManager manager = activity.getSupportFragmentManager();
        Fragment tabFragment = manager.findFragmentById(android.R.id.tabcontent);
        // check if is opened inside an actionbar tab, which is
        // another fragment
        if (tabFragment != null) {
            manager = tabFragment.getChildFragmentManager();
        }
        FragmentTransaction transaction = null;
        transaction = manager.beginTransaction();
        transaction.remove(fragment);
        transaction.commitAllowingStateLoss();
        return transaction;
    }

    public static int getColorAccent(final Context context) {
        TypedArray values = null;
        try {
            int resourceId = TiRHelper.getResource("android.support.v7.appcompat.R$", "attr.colorAccent");
            int[] attrs = { resourceId };
            values = context.getTheme().obtainStyledAttributes(attrs);
            return values.getColor(0, 0);
        } catch (ResourceNotFoundException e) {
            return 0;
        } finally {
            if (values != null) {
                values.recycle();
            }
        }
    }
}