android 離線下載

風的王子發表於2013-05-23

==========================離線下載

import java.io.BufferedInputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


import org.json.JSONException;
import org.json.JSONObject;


import com.rytong.tools.crypto.AESCipher;
import com.rytong.tools.crypto.Base64;
import com.rytong.tools.crypto.HMac;
import com.rytong.tools.httpconnect.HttpManager;
import com.rytong.tools.utils.Utils;


import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.os.Looper;
import android.util.DisplayMetrics;


public class OffStoreDownload {
    private final String PLUGRESOURCES = "plug_in_resources.db";
    private final String OFFLINERESOURCES = "offline_resources.db";
    private Activity activity_;
    // 是否必須更新(-1:不更新,0:不強制更新,1:強制更新)
    private String mustupate_;
    // 離線資源資料庫
    public static OffStoreDB offLineDB_;
    // 外掛資料庫
    public static OffStoreDB plugDB_;
    
    static public String FILEROOT;
    
    final static public String PLUGROOT = "plug-in-resources/";
    final static public String OFFLINEROOT = "offline-resources/";
    final static public String WRITEROOT = "write/";
    // 需要更新資源列表
    private HashMap<String, String> resourceMap_;
    // 列表path和rev(SHA1的hash)
    private HashMap<String, HashMap<String, String>> serverMap_;
    private HashMap<String, Boolean> alreadyDownload_;
    private String serverString_;
    // 離線更新介面
    public final String RESOURCE_UPDATE = "/ota/resource_update?";
    private int downloadNum_;


    public OffStoreDownload(Activity activity) {
        activity_ = activity;
        alreadyDownload_ = new HashMap<String, Boolean>();
        downloadNum_ = 1;// 重複下載一次即可。
        offLineDB_ = new OffStoreDB(activity, OFFLINERESOURCES);
        plugDB_ = new OffStoreDB(activity, PLUGRESOURCES);
    }


    // 步驟1:傳送請求:平臺型別、螢幕解析度、server.desc(不存在則為空)
    public void downloadOfflineResource() {
        // 手機平臺
        final String platform = Utils.getConfigStringFormAsset(activity_, "offstoreplatform");
        // 解析度
        DisplayMetrics dm = new DisplayMetrics();
        activity_.getWindowManager().getDefaultDisplay().getMetrics(dm);
        final String screenResolution = String.valueOf(dm.widthPixels) + "*" + String.valueOf(dm.heightPixels);
        // 本地離線檔案client.desc(不為空則傳送)
        final String client_desc = (String) offLineDB_.find("client.desc");
        Timer timer_ = new Timer();
        timer_.schedule(new TimerTask() {
            @Override
            public void run() {
                HttpManager hm = new HttpManager(activity_);
                String uri = Utils.getConfigStringFormAsset(activity_, "SERVER_URI").concat(RESOURCE_UPDATE);// http://192.168.85.69:4002/ota/resource_update?
                StringBuffer buf = new StringBuffer();
                buf.append("desc=");
                if (client_desc != null)
                    buf.append(client_desc).append("&");
                else
                    buf.append("&");
                buf.append("platform=").append(platform).append("&");
                buf.append("resolution=").append(screenResolution);
                String body = buf.toString();
                Object server_update = hm.sendPostRequest(uri, body, HttpManager.MIME_JSON, null);
                // 步驟2:讀取返回的download.desc和server.desc。
                parseResponse(server_update);
            }
        }, 0);
    }


    public void saveToDb(OffStoreDB db, String key, Object value) {
        db.insert(key, value);
    }


    // 解析返回來的資料
    protected void parseResponse(Object serverUpdate) {
        if (null == serverUpdate)
            return;
        if (serverUpdate instanceof String)
            parseJSON((String) serverUpdate);
    }


    // 先判斷是否需要強制更新,如果不是強制更新,則提醒使用者。
    private void parseJSON(String serverResponse) {
        try {
            // {"mustupdate":0,"server":{"yybk.zip":{"rev":"0b08d0c498e0eb5e2d19abc6dda695cc7f462473","path":"android/480-320/zip","desc":{"html/showCard.html":"0afd92a6b7fa6070a3610ffad94d24c0137538d1","imgs/back_03.png":"91e81ea8d58fcca77f4e43699e3cc8186691cdb2","imgs/card1.png":"f4598e2ea5d5fee387cb906506a424e1e842aac7","imgs/card2.png":"c42659dd3ab85894685ea7b10bcd2755b6d7b2cc","imgs/card3.png":"debe586a42d9b3536574bcd1daaaafb67332e99a","imgs/photo1.png":"14fbab85d86ce08593b67153a86daa8085fec61f","imgs/title_02.png":"63f7b712014402807f35d37b34acfc557228e7d2","js/jquery-1.7.1.js":"62922be0191a2cc4d13ef3abe4a62d1ebc17492d"}}},"download":{"yybk.zip":"http://192.168.65.82:4004/ebank/resources/android/480-320/zip/yybk.zip"}};
            // {"mustupdate":0,"server":{"colorful.png":{"rev":"7915a251510c265826a1ee14c5a7c1ac31459890","path":"android/320*480/png"},"background_cao.png":{"rev":"da39a3ee5e6b4b0d3255bfef95601890afd80709","path":"android/320*480/png"}},"download":{"background_cao.png":"http://192.168.63.68:4002/ebank/resources/android/320*480/png/background_cao.png","colorful.png":"http://192.168.63.68:4002/ebank/resources/android/320*480/png/colorful.png"}}
            Utils.printOutToConsole("offstore data:  " + serverResponse);
            final JSONObject obj = new JSONObject(serverResponse);
            // 是否必須更新
            mustupate_ = obj.getString("mustupdate");
            if (null == mustupate_)
                return;
            if (mustupate_.equalsIgnoreCase("0")) {
                final Builder ad = new AlertDialog.Builder(activity_);
                ad.setTitle("提示").setMessage("您有新的離線資源需要下載,是否更新?").setCancelable(false)
                        .setNegativeButton("確定", new DialogInterface.OnClickListener() {


                            public void onClick(DialogInterface dialog, int which) {
                                // TODO Auto-generated method stub
                                parseJSONIndeed(obj);
                                if (checkAllDownload()) {
                                    // 下載10:下載完畢後儲存server.desc檔案。
                                    saveToDb(offLineDB_, "client.desc", serverString_);
                                    return;
                                }
                                downloadResourceByPath();// 根據路徑下載每一個resource
                            }


                        }).setPositiveButton("取消", new DialogInterface.OnClickListener() {


                            public void onClick(DialogInterface dialog, int which) {
                                // TODO Auto-generated method stub
                            }


                        });
                if (!activity_.isFinishing()) {
                    activity_.runOnUiThread(new Runnable() {


                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            ad.show();
                        }


                    });
                }


            } else {
                parseJSONIndeed(obj);
                if (checkAllDownload()) {
                    // 下載10:下載完畢後儲存server.desc檔案。
                    saveToDb(offLineDB_, "client.desc", serverString_);
                    return;
                }
                downloadResourceByPath();// 根據路徑下載每一個resource
            }


        } catch (JSONException e) {
            Utils.printException(e);
        }
    }


    // 解析JSON---步驟3:解析download.desc。
    private void parseJSONIndeed(JSONObject obj) {
        // TODO Auto-generated method stub
        // download.desc
        try {
            resourceMap_ = new HashMap<String, String>();
            serverMap_ = new HashMap<String, HashMap<String, String>>();
            JSONObject downObject;


            downObject = obj.getJSONObject("download");


            Iterator itDown = downObject.keys();
            String key = "";
            String value = "";
            while (itDown.hasNext()) {
                key = itDown.next().toString();
                value = downObject.getString(key);
                // 未下載時,更新標誌為false;更新後修改標誌位
                alreadyDownload_.put(key, false);
                resourceMap_.put(key, value);
            }


            // server.desc
            JSONObject serverObject = obj.getJSONObject("server");
            serverString_ = serverObject.toString();
            Iterator itServer = serverObject.keys();
            key = "";
            JSONObject jsonValue;
            String path;
            String rev;
            while (itServer.hasNext()) {
                key = itServer.next().toString();
                jsonValue = serverObject.getJSONObject(key);
                HashMap<String, String> params = new HashMap<String, String>();
                path = jsonValue.getString("path");
                rev = jsonValue.getString("rev");
                params.put("path", path);
                params.put("rev", rev);
                serverMap_.put(key, params);
            }
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
    }


    // 步驟5:根據download.desc下載列表建立任務佇列task-list。
    public void downloadResourceByPath() {
        try {
            String fileName = null, filePath = null, shaFile = null, serverFileName = null, serverPath = null, serverRev = null;
            byte[] fileString = null;
            if (resourceMap_ == null)
                return;


            Iterator it = resourceMap_.keySet().iterator();
            HashMap<String, String> params;
            while (it.hasNext()) {
                fileName = it.next().toString();
                filePath = resourceMap_.get(fileName);
                if (checkDownload(fileName)) {
                    // 已經下載過,返回
                    return;
                } else {
                    // 下載每個檔案
                    fileString = downloadPerResource(filePath);
                    File file = Utils.getFileFromBytes(fileString, fileName);
                    if (Utils.getMIMEType(file).equalsIgnoreCase("zip")) {
                        // 如果資料是壓縮的則應進行解壓操作
                        unZip(new ByteArrayInputStream(fileString));
                        // fileString = Utils.gunzip(fileString);
                    }
                    file = null;
                    // 計算檔案SHA1值
                    if (fileString != null) {
                        byte[] temp = HMac.SHA1(fileString);
                        shaFile = HMac.byteArrayToHexString(temp);
                    }
                }
                // 查詢第二個Map中含有第一個Map的key
                if (serverMap_.containsKey(fileName)) {
                    params = serverMap_.get(fileName);
                    serverPath = params.get("path");
                    serverRev = params.get("rev");
                }
                // 步驟6:每一個資原始檔下載完畢後計算資原始檔的SHA1值,並與server.desc記錄的SHA1值進行比較。(遞迴)
                comparedRedownload(fileName, shaFile, filePath, fileString, serverRev);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
    }


    private void unZip(InputStream input) {
        // TODO Auto-generated method stub
        ZipInputStream zis = new ZipInputStream(new BufferedInputStream(input));
        try {
            ZipEntry ze;
            while ((ze = zis.getNextEntry()) != null) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
                byte[] buffer = new byte[1024];
                int count;
                while ((count = zis.read(buffer)) != -1) {
                    baos.write(buffer, 0, count);
                }
                String filename = ze.getName();
                byte[] bytes = baos.toByteArray();
                // do something with 'filename' and 'bytes'...
                saveToDb(plugDB_, filename, bytes);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        } finally {
            try {
                if (null != zis)
                    zis.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Utils.printException(e);
            }
        }


    }


    /**
     * @param fileName
     *            客戶端檔名
     * @param shaFile
     *            客戶端計算出的SHA1
     * @param filePath
     *            客戶端下載檔案的路徑
     * @param fileString
     *            客戶端下載檔案後的位元組陣列
     * @param serverFileName
     *            伺服器端檔名
     * @param serverRev
     *            伺服器端SHA1
     */
    private void comparedRedownload(String fileName, String shaFile, String filePath, byte[] fileString,
            String serverRev) {
        try {
            boolean noModify = comparedSHA1(fileName, shaFile, serverRev);
            if (noModify) {
                // 步驟7:驗證資原始檔的正確性後,以name為key儲存資原始檔。
                // 下載完的檔案儲存到資料庫


                offLineDB_.deleteToDB(fileName);
                if (fileName.contains(".png"))
                    saveToDb(offLineDB_, fileName, fileString);
                else
                    saveToDb(offLineDB_, fileName, new String(fileString, "UTF-8"));


                // 步驟8:更新download.desc的下載狀態標記,標記為已下載。
                // 儲存到資料庫後修改"已下載"的標誌位
                alreadyDownload_.put(fileName, true);
            } else {
                if (downloadNum_-- > 0) {
                    // 重新下載每個檔案
                    fileString = downloadPerResource(filePath);
                    File file = Utils.getFileFromBytes(fileString, fileName);
                    if (Utils.getMIMEType(file).equalsIgnoreCase("zip")) {
                        // 如果資料是壓縮的則應進行解壓操作
                        fileString = Utils.gunzip(fileString);
                    }
                    file = null;
                    // 重新計算檔案SHA1值
                    byte[] disg = HMac.SHA1(fileString);
                    if (disg != null) {
                        shaFile = disg.toString();
                    }
                    comparedRedownload(fileName, shaFile, filePath, fileString, serverRev);
                }
            }
        } catch (UnsupportedEncodingException e) {
            Utils.printException(e);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
    }


    // 判斷檔名為fileName的檔案是否下載過
    private boolean checkDownload(String fileName) {
        if (alreadyDownload_ != null) {
            return alreadyDownload_.get(fileName);
        }
        return false;
    }


    // 判斷所有的檔案是否下載完
    private boolean checkAllDownload() {
        if (alreadyDownload_ != null) {
            for (Map.Entry<String, Boolean> entry : alreadyDownload_.entrySet()) {
                boolean entryDownload = entry.getValue();
                // 如果有值為false,即為沒有下載完,所有的都為true,才為全部下載完成
                if (!entryDownload)
                    return false;
            }
            // 全部下載完成
            return true;
        }
        return false;
    }


    // 比較下栽檔案的SHA1和server.desc
    private boolean comparedSHA1(String fileName, String shaFile, String serverSHA1) {
        if (fileName != null && shaFile != null & serverSHA1 != null) {
            if (shaFile.equalsIgnoreCase(serverSHA1)) {
                return true;
            }
        }
        return false;
    }


    // 下載檔案列表中的每一個檔案
    private byte[] downloadPerResource(String filePath) {
        ByteArrayOutputStream fileBuffer = new ByteArrayOutputStream(1024);
        byte[] fileBytes = null;
        try {
            HttpManager hm = new HttpManager(activity_);
            hm.read(filePath, fileBuffer, null);
            fileBytes = fileBuffer.toByteArray();
        } catch (Exception e) {
            Utils.printException(e);
        } catch (OutOfMemoryError oe) {
            Utils.printOutToConsole(oe);
        } finally {
            if (null != fileBuffer)
                try {
                    fileBuffer.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    Utils.printException(e);
                }
            fileBuffer = null;
        }
        return fileBytes;
    }


    public boolean getFirstLogin() {
        File file = new File(activity_.getFilesDir(), offLineDB_.DATABASE_NAME);
        return file.exists();
    }


    /**
     * 不聯網情況下個別頁面讀取本地資料庫XML
     * 
     * @param itemValue
     * @param currentView_
     * @return
     */
    public Object readOfflineResource(OffStoreDB db, String itemValue) {
        return db.find(itemValue);
    }


    public static void setRootPath() {
        FILEROOT = Utils.getActivity().getFilesDir().getPath().concat("/");
    }


}


===============================================================================

//資料庫操作


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


public class OffStoreDB extends SQLiteOpenHelper {


    public static String DATABASE_NAME = "emp.db";
    private static int DATABASE_VERSION = 1;
    private static String TABLE_NAME = "emp_table";
    private static String COLUMN_ID = "_id";
    private static String COLUMN_NAME = "name";
    private static String COLUMN_VALUE = "value";


    public OffStoreDB(Context context, String name) {
        super(context, name, null, DATABASE_VERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table ".concat(TABLE_NAME).concat("(").concat(COLUMN_ID)
                .concat(" integer primary key autoincrement,").concat(COLUMN_NAME).concat(" varchar(20),")
                .concat(COLUMN_VALUE).concat(" varchar(100))"));
    }


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS ".concat(TABLE_NAME));
        onCreate(db);
    }


    public long insert(String column_name, Object column_value) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_NAME, column_name);
        if (column_value instanceof String)
            cv.put(COLUMN_VALUE, (String) column_value);
        else if (column_value instanceof byte[])
            cv.put(COLUMN_VALUE, (byte[]) column_value);
        long row = db.insert(TABLE_NAME, null, cv);
        return row;
    }


    public Object find(String name) {
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.rawQuery("select value from emp_table where name = ?", new String[] { name });
        // select value from emp_table where name = "fwzx.xml";
        if (cursor.moveToNext()) {
            Object value = null;
            try {
                // 如果是圖片將會被儲存為位元組陣列形式
                if (name.contains(".png"))
                    value = cursor.getBlob(cursor.getColumnIndex(COLUMN_VALUE));
                else
                    value = cursor.getString(cursor.getColumnIndex(COLUMN_VALUE));
                cursor.close();
            } catch (Exception e) {
                Utils.printException(e);
            }
            return value;
        } else {
            cursor.close();
            return null;
        }
    }


    public int deleteToDB(String fileName) {
        SQLiteDatabase db = this.getWritableDatabase();
        // ContentValues cv = new ContentValues();
        // cv.put(COLUMN_NAME, fileName);
        return db.delete(TABLE_NAME, COLUMN_NAME + "=?", new String[] { fileName });
    }
}


-----------------------------------------------utils

//#define Android2.2
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Vector;


import java.util.UUID;


import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONObject;


import com.rytong.tools.clienthello.ClientHello;
import com.rytong.tools.crypto.AESCipher;
import com.rytong.tools.crypto.HMac;
import com.rytong.tools.crypto.MD5;
import com.rytong.tools.crypto.RSACipher;
import com.rytong.tools.httpconnect.HttpManager;
import com.rytong.tools.offstore.OffStoreDownload;
import com.rytong.tools.ui.Component;
import com.rytong.tools.ui.LPLayout;


import android.R.color;
import android.R.integer;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.telephony.CellLocation;
import android.telephony.TelephonyManager;
// #ifdef Android2.2
import android.telephony.cdma.CdmaCellLocation;
// #endif
import android.telephony.gsm.GsmCellLocation;
import android.text.TextPaint;
import android.util.DisplayMetrics;
import android.view.View;


/**
 * Utility class.
 */
public final class Utils {
    private static int BRHEIGHT;
    private static Activity activity_;
    // 是否列印標誌
    private static boolean isPrintMessage_;
    // 字型縮放引數,如果手機解析度和密度同比增長,則該引數?,表示不需縮放,如果不匹配,則需校正該引數設?
    public static float SCALEDFONT;
    // 基準螢幕的寬,該值應和服務端定義xml的基準螢幕保持?????
    public static int BenchmarkresolutionW_;
    // 基準螢幕的高,該值應和服務端定義xml的基????幕保持一?
    public static int BenchmarkresolutionH_;
    /**
     * Used for gzip. Re. rfc1951.
     */
    static int gIndex = 0;
    static int bitByte = 0;
    static int bitIndex = 0;
    private final static int MAX_BITS = 16;
    private final static int[] EXTRA_L_BITS = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
            5, 5, 5, 5, 0 };
    private final static int[] EXTRA_L_VALUES = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51,
            59, 67, 83, 99, 115, 131, 163, 195, 227, 258 };
    private final static int[] EXTRA_D_BITS = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
            10, 11, 11, 12, 12, 13, 13 };
    private final static int[] EXTRA_D_VALUES = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
            513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 };
    private final static int[] DYNAMIC_L_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };


    // 介面X方向參考縮放參?此引數為控制元件寬或者字型大小的參考??
    public static float SCALEDATEX;
    // 介面Y方向參考縮放參?此引數為控制元件高的參考??
    public static float SCALEDATEY;
    /** Default alpha value used to draw menu panels and status bars. */
    public final static int DEFAULT_TRANSPARENCY = 0xa0000000;
    final static int OK_KEY = -5;
    final static int LEFT_SOFTKEY = -6;
    final static int RIGHT_SOFTKEY = -7;


    // if it is sony ericsson, SONY_ERICSSON is true.
    public final static boolean SONY_ERICSSON = false;
    // 日誌名稱
    public static final String LOGNAME = "LPC";
    /**
     * Used as indices in text parser's return result of a line break in a string.
     */
    public final static int LBI_BREAK = 0; // Break position (this char is in current
    // line)
    public final static int LBI_NEXT = 1; // Next char position to resume scanning (to
    // skip tags)
    public final static int LBI_TYPE = 2; // Line break type: newline, paragraph, ...,
    // defined below.
    public final static int LBI_START = 3; // Starting position of this line.
    public final static int LBI_SIZE = 4; // Number of linebreak indices.


    /**
     * Types of line breaks.
     */
    final static int LB_NEWLINE = 0;
    final static int LB_PARAGRAPH = 1;
    final static int LB_SKIP = 2; // Skip characters within [LBI_BREAK,
    // LBI_NEXT)
    final static int LB_HYPHEN = 3;


    /**
     * The number of alpha transparency levels。will be reset by numAlphaLevels() in LPMid.java. The minimum
     * numAlphaLevel_ is 2, which indicates only support for full transparency and full opacity.
     */
    final static int numAlphaLevel_ = 256;


    final static int BLUE = 0xFF0000FF;
    final static int WHITE = 0xFFFFFFFF;
    final static int BLACK = 0xFF000000;
    final static int GREY = 0xFF808080;
    final static int SELECTED_LINK_COLOR = 0xFF808080;
    public final static int TABBARBACKGROUNDCOLOR = 0xFF294B78;


    public final static char MATCH = '\"';


    /** True if the user selects item Map */
    public static boolean toMapView_;


    final static String SPLIT_CHARACTERS = " ,.";


    // 呼叫系統相機的請求碼
    public final static int CAMERA_REQUEST_CODE = 1001;


    // 解讀並儲存assets資料夾下的Config.txt檔案資訊
    private static HashMap<String, String> configHm_;


    // 連點事件一時間間隔
    public static int jat_lag = 0;


    /**
     * @return
     */
    public final static byte[] getClientGMTUnixTime() {
        // get local time.4 bytes.
        Calendar cal = Calendar.getInstance();
        Date date = cal.getTime();
        int hours = date.getHours();
        byte[] clientGmtUnixTime = new byte[4];
        clientGmtUnixTime[0] = (byte) ((hours & 0x0000FF00) >> 8);
        clientGmtUnixTime[1] = (byte) ((hours & 0x000000FF));
        int minutes = date.getMinutes();
        clientGmtUnixTime[2] = (byte) ((minutes & 0x0000FF00) >> 8);
        clientGmtUnixTime[3] = (byte) ((minutes & 0x000000FF));
        return clientGmtUnixTime;
    }


    // /**
    // * 從資源圖片裡面獲取指定檔名的圖片,如果沒有就返回空
    // *
    // * @param 指定的要查詢的檔名
    // * @param 介面物件
    // * @return
    // */
    // public final final static Bitmap getBitmapFromDrawable(Activity bv, String bitmapName) {
    // bitmapName = bitmapName.trim();
    // if (bitmapName.endsWith(".png")) {
    // // 服務端傳送過來的圖片名稱有可能帶?png字尾,先去掉字尾再進行相關操作比?
    // bitmapName = bitmapName.substring(0, bitmapName.lastIndexOf(".png"));
    // }
    // // 利用反射機制獲取物件域名
    // Object obj = null;
    // String varName = null;
    // Field[] fields = R.drawable.class.getDeclaredFields();
    // int size = fields.length;
    // for (int i = 0, len = size; i < len; i++) {
    // // 對於每個屬性,獲取屬性名
    // varName = fields[i].getName();
    // if (varName.equalsIgnoreCase(bitmapName)) {
    // try {
    // // 獲取原來的訪問控制權?
    // boolean accessFlag = fields[i].isAccessible();
    // // 修改訪問控制許可權
    // fields[i].setAccessible(true);
    // // 獲取在物件f中屬性fields[i]對應的物件中的變?
    // obj = fields[i].get(varName);
    // // ????訪問控制許可權
    // fields[i].setAccessible(accessFlag);
    // int id = Integer.parseInt(obj.toString());
    // Bitmap bm = getBitmap(id, bv);
    // return bm;
    // } catch (IllegalArgumentException ex) {
    // printException(ex);
    // } catch (IllegalAccessException ex) {
    // printException(ex);
    // }
    // }
    // }
    // return null;
    // }
    /**
     * 傳送端?
     * 
     * @param phoneNum
     * @param content
     */
    public final static void sendSms(int phoneNum, String content) {
        Intent intent = new Intent();
        // 系統預設的action,用來開啟預設的簡訊界?
        intent.setAction(Intent.ACTION_SENDTO);
        // 需要發短息的號?
        intent.setData(Uri.parse("smsto:" + phoneNum));
        intent.putExtra("sms_body", content);
        activity_.startActivity(intent);
    }


    /**
     * 獲取資源圖片
     * 
     * @param context
     * @param resourcesName
     * @return
     */
    public final static Bitmap getBitmapFromResources(Context context, String resourcesName) {
        Bitmap bitmap = null;
        if (resourcesName == null)
            return bitmap;


        Object img = null;
        // 先補?png字尾名從sql中取出對應的圖片
        resourcesName = getPNGName(resourcesName);
        img = OffStoreDownload.plugDB_.find(resourcesName);
        if (null != img && img instanceof byte[]) {
            byte[] temp = (byte[]) img;
            bitmap = BitmapFactory.decodeByteArray(temp, 0, temp.length);
            return bitmap;
        }


        img = OffStoreDownload.offLineDB_.find(resourcesName);
        if (null != img && img instanceof byte[]) {
            byte[] temp = (byte[]) img;
            bitmap = BitmapFactory.decodeByteArray(temp, 0, temp.length);
            return bitmap;
        }
        // 如果sql中沒有儲存對應的圖片,則將字尾去掉,在資原始檔裡面搜尋
        if (resourcesName.endsWith(".png")) {
            resourcesName = resourcesName.substring(0, resourcesName.lastIndexOf(".png"));
        }
        resourcesName = resourcesName.toLowerCase();
        int resourcesId = getResourcesId(context, resourcesName, "drawable");
        if (resourcesId != 0) {
            bitmap = getBitmap(resourcesId, (Activity) context);
        }


        return bitmap;
    }


    /**
     * 獲取資源id
     * 
     * @param context
     * @param resourcesName
     * @param resourcesType
     * @param packageName
     * @return
     */
    public final static int getResourcesId(Context context, String resourcesName, String resourcesType) {
        int resourcesId = 0;
        resourcesId = context.getResources().getIdentifier(resourcesName, resourcesType, context.getPackageName());
        return resourcesId;
    }


    /**
     * 獲取專案Config中的app名稱,用於聯網後臺區別報?
     * 
     * @param activity
     */
    public final static void getAppName(Activity activity) {
        HttpManager.APPNAME = getConfigStringFormAsset(activity, "app");
    }


    public final static void getPlatform(Activity activity) {
        HttpManager.OPLATFORM = getConfigStringFormAsset(activity, "oPlatform");
    }


    public final static String getClientType(Activity activity) {
        return getConfigStringFormAsset(activity, "clientType");
    }


    /**
     * 判斷字串是否以.png結尾,如果不是則加上該字尾
     */
    public final static String getPNGName(String name) {
        name = name.trim();
        if (!name.endsWith(".png")) {
            if (name.indexOf(".") != -1)
                name = name.substring(0, name.indexOf("."));
            name = name.concat(".png");
        }
        return name;
    }


    public final static Bitmap getBitmap(int rdoing, Activity bv) {
        // TODO Auto-generated method stub
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Bitmap.Config.RGB_565;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        InputStream is = bv.getResources().openRawResource(rdoing);


        Bitmap img = BitmapFactory.decodeStream(is, null, opt);
        try {
            is.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
        return img;
    }


    public final static int getColorValue(String value) {
        String colorstr = "";
        try {
            colorstr = getValidColorString(value);


            if (colorstr.length() > 6) {
                int color = Integer.parseInt(colorstr, 16);
                color |= 0x00000000;
                return color;
            } else {
                int color = Integer.parseInt(colorstr, 16);
                color |= 0xFF000000;
                return color;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
            return 0xFFFFFFFF;
        }


    }


    /**
     * The valid color string is a 6 bit hex number. e.g., FFFFFF.
     * 
     * @param srcColorstr
     * @return
     */
    public final static String getValidColorString(final String srcColorstr) throws Exception {
        String colorstr = srcColorstr;
        if (colorstr.startsWith("#")) {
            colorstr = colorstr.substring(1);
        }
        return colorstr;
    }


    // 將字串陣列中的內???轉換成????????方便字元陣列檢?
    public final static String getArrayString(String[] sa) {
        if (null == sa)
            return "";
        return Arrays.toString(sa);
    }


    final static boolean isLeftSoftKey(int key) {
        return (key == LEFT_SOFTKEY);
    }


    final static boolean isRightSoftKey(int key) {
        // Motorola some phones's right key is -22, and other is 22.
        // return (key == RIGHT_SOFTKEY || key == -RIGHT_SOFTKEY);
        return (key == RIGHT_SOFTKEY);
    }


    final static int getLeftSoftKey() {
        return LEFT_SOFTKEY;
    }


    final static int getRightSoftKey() {
        return RIGHT_SOFTKEY;
    }


    /**
     * 
     * 把位元組陣列儲存為一個文?
     * 
     */
    public final static File getFileFromBytes(byte[] b, String outputFile) {


        String pathString = activity_.getFilesDir().getPath().concat("/");
        BufferedOutputStream stream = null;


        File file = null;


        try {


            file = new File(pathString.concat(outputFile));
            file.createNewFile();
            FileOutputStream fstream = new FileOutputStream(file);


            stream = new BufferedOutputStream(fstream);


            stream.write(b);


        } catch (Exception e) {


            printException(e);


        } finally {


            if (stream != null) {


                try {


                    stream.close();


                } catch (IOException e1) {


                    printException(e1);


                }


            }


        }


        return file;


    }


    /**
     * 根據檔案字尾名返回檔案類?
     * 
     * @param fileTemp
     * @return
     */
    public final static String getMIMEType(File fileTemp) {
        // TODO Auto-generated method stub
        String type = "";
        String fName = fileTemp.getName();
        String end = fName.substring(fName.lastIndexOf(".") + 1, fName.length()).toLowerCase();
        if (end.equals("m4a") || end.equals("mp3") || end.equals("mid") || end.equals("xmf") || end.equals("ogg")
                || end.equals("wav")) {
            type = "audio";
        } else if (end.equals("3gp") || end.equals("mp4")) {
            type = "video";
        } else if (end.equals("jpg") || end.equals("gif") || end.equals("png") || end.equals("jpeg")
                || end.equals("bmp")) {
            type = "image";
        } else if (end.equals("apk")) {
            type = "application/vnd.android.package-archive";
        } else if (end.equals("zip")) {
            type = "zip";
        } else if (end.equals("apk")) {


        } else {
            type += "/*";
        }
        return type;
    }


    /**
     * Graphics.drawRegion() has a bug in 4.2.1 and we have to use a replacement function.
     */
    public final static void drawRegion(Canvas g, Bitmap img, int srcX, int srcY, int width, int height, int dstX,
            int dstY) {
        g.drawBitmap(img, new Rect(srcX, srcY, srcX + width, srcY + height), new Rect(dstX, dstY, dstX + width, dstY
                + height), new Paint());
    }


    public final static int fixTransparentPixel(int pixel) {
        // We don't use semitransparent pixel if the phone only support for full
        // transparency and full opacity.
        if (numAlphaLevel_ > 2 && (pixel & 0xff000000) != 0)
            return (pixel & 0x00ffffff) + DEFAULT_TRANSPARENCY;
        else
            return pixel;
    }


    /**
     * Given an image, assign transparency value to its non-background pixels (background pixels are pixels that are
     * completely transparent).
     * 
     * @param transparency
     *            255 == totally opaque.
     */
    public final static Bitmap fixTransparentImage(Bitmap img) throws Exception {
        if (null == img)
            return null;
        int width = img.getWidth();
        int height = img.getHeight();
        int sz = width * height;
        int[] rgb = new int[sz];
        // Params: (rgb, offset, scanlength, x, y, width, height)
        // rgbData[offset + (a - x) + (b - y) * scanlength] = P(a, b);
        img.getPixels(rgb, 0, width, 0, 0, width, height);
        for (int i = 0; i < sz; ++i) {
            rgb[i] = fixTransparentPixel(rgb[i]);
        }
        img = Bitmap.createBitmap(rgb, width, height, Bitmap.Config.RGB_565);
        return img;
    }


    /*
     * Check if the image is over 4kB or not.
     * 
     * @param name the full name with a path of an image file.
     * 
     * @return
     */
    // private final final static final boolean isImgBelow4KB(String name) {
    // boolean isNotOver = ConfigManager.IMAGE_TRANSPARENCY;
    // try {
    // int imgSize = 0;
    // InputStream is = null;
    // is = new Object().getClass().getResourceAsStream(name);
    // imgSize = is.available();
    // if (imgSize < 1024 * 4){
    // isNotOver = true;
    // }
    // } catch (Exception e) {
    // printOutToConsole("LPUtils.isImgOver4KB(): " + e.toString());
    // }
    // return isNotOver;
    // }


    public final static String abbrevString(String title, Paint font, int maxWidth) {
        if (title == null)
            return "";
        if (font.measureText(title) > maxWidth) {
            int pos = nextLinePosition(title.toCharArray(), 0, font, maxWidth,
                    font.getTextWidths(ELLIPSIS, 0, ELLIPSIS.length, new float[ELLIPSIS.length]));
            if (pos == -1)
                return title;
            else
                return title.substring(0, pos) + "...";
        } else
            return title;
    }


    final static String appendDots(String src, Paint font, int maxWidth) {
        if (src == null || font == null || maxWidth < 0) {
            return null;
        }
        String dest = null;
        dest = src + "...";
        while (font.measureText(dest) > maxWidth) {
            dest = src.substring(0, src.length() - 2) + "...";
        }
        return dest;
    }


    private final static char ELLIPSIS[] = { '.', '.', '.' };


    public final static int getEllipsisWidth(Paint f) {
        return f.getTextWidths(ELLIPSIS, 0, ELLIPSIS.length, new float[ELLIPSIS.length]);
    }


    /**
     * Is this character whitespace?
     */
    public final static boolean isWhitespace(char ch) {
        return (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t');
    }


    /**
     * Case insensitive string match
     * 
     * @param ignoreCase
     *            if true, ignore case
     * @param s1
     * @param s2
     */
    public final static boolean stringMatch(boolean ignoreCase, String s1, String s2) {
        int cmplen = Math.max(s1.length(), s2.length());
        if ((s1 == null) || (s2 == null)) {
            return false;
        }
        return s1.regionMatches(true, 0, s2, 0, cmplen);
    }


    /**
     * Given a starting position in a string, return the next position that this string should be split if it cannot fit
     * within the given width. If it does not fit, we will include an ellipsis at the end to indicate broken line. If it
     * does fit, we return -1.
     */
    public final static int nextLinePosition(char[] str, int pos, Paint f, int maxWidth, int ellWidth) {
        int strWidth = 0;
        while (pos < str.length) {
            if (str[pos] == '\n') {
                return pos;
            }
            strWidth += f.measureText(String.valueOf(str[pos]));
            if (strWidth + ellWidth > maxWidth) {
                // If the rest of string can fit instead of the ellipsis, put it
                // there.
                // int tmp = f.getTextWidths(str, pos + 1, str.length - pos - 1,
                // new float[str.length - pos - 1]);
                int tmp = (int) f.measureText(str, pos + 1, str.length - pos - 1);
                if (strWidth + tmp <= maxWidth) {
                    return -1;
                } else {
                    return pos;
                }
            }
            ++pos;
        }
        return -1;
    }


    /**
     * Starting from pos in str, find the longest string that fits in maxLineWidth. If doesn't fit, break the line by
     * hyphenation or at a line break point.
     * 
     * If newLine is true, we are starting from a new line and should get rid of initial white spaces.
     * 
     * Returns the position of the next character to be scanned in str after this line break.
     */
    public final static int nextLineLayout(char[] str, int pos, Paint f, int maxLineWidth, int hyphenWidth,
            int[] linebreak, boolean newLine) {
        int strWidth = 0;
        int startPos = pos;
        int lastBreakPoint = pos;


        // Filter out initial white spaces at start of a line.
        if (newLine) {
            while (pos < str.length && (str[pos] == ' ' || str[pos] == '\t'))
                ++pos;
        }


        linebreak[LBI_START] = pos;
        while (pos < str.length) {
            boolean punct = false;
            switch (str[pos]) {
            // NOTE: don't recognize CR because in XML, only <br> or <p> should
            // matter.
            case '\n':
                // Insert a newline.
                linebreak[LBI_BREAK] = pos - 1;
                linebreak[LBI_NEXT] = pos + 1;
                linebreak[LBI_TYPE] = LB_SKIP;
                return pos + 1;
            case '<': {
                // Look ahead to see if we have a <p>, <br>, <p/>, <br/>, </p>,
                // </br>, <b> <tr> <td>
                // XXX We ignore complex cases, such as newlines are inserted
                // into tags.
                // NOTE: the 32 limit is an arbitrary one
                int endPos = pos;
                while (endPos < (str.length - 1) && endPos - pos < 8) {
                    ++endPos;
                    if (str[endPos] == '>') {
                        int pp1 = pos + 1;
                        int pp2 = pos + 2;
                        int pp3 = pos + 3;
                        char ch_pp1 = Character.toLowerCase(str[pp1]);
                        if (ch_pp1 == 'p' && (pp2 == endPos || str[pp2] == '/' || str[pp2] == ' ')) {
                            linebreak[LBI_BREAK] = pos - 1;
                            linebreak[LBI_NEXT] = endPos + 1;
                            linebreak[LBI_TYPE] = LB_PARAGRAPH; // Paragraph
                            return endPos + 1;
                        } else if (ch_pp1 == 'b'
                                && (pp2 == endPos || (Character.toLowerCase(str[pp2]) == 'r' && (pp3 == endPos
                                        || str[pp3] == '/' || str[pp3] == ' ')))) {
                            linebreak[LBI_BREAK] = pos - 1;
                            linebreak[LBI_NEXT] = endPos + 1;
                            // Only add newline if this is a br.
                            linebreak[LBI_TYPE] = (pp2 == endPos) ? LB_SKIP : LB_NEWLINE;
                            return endPos + 1;
                        } else if (ch_pp1 == '/' || ch_pp1 == 'i' || ch_pp1 == 'e' || ch_pp1 == 's' || ch_pp1 == 'c'
                                || ch_pp1 == 't' || ch_pp1 == 'h') {


                            // Ignore <em>, <strong>, <center>
                            // Ignore all kinds of end tags.
                            linebreak[LBI_BREAK] = pos - 1;
                            linebreak[LBI_NEXT] = endPos + 1;
                            linebreak[LBI_TYPE] = LB_SKIP;
                            return endPos + 1;
                        }
                    }
                }
                // XXX FIXME: Ignore <a href=...> and <font ...>, <a target...>,
                // <a title...>, <a rel...> here. We will capture the hyper link
                // and tie it to the link body later.
                char ch_pp1 = Character.toLowerCase(str[pos + 1]);
                if (str.length - pos >= 5 && (ch_pp1 == 'a' || ch_pp1 == 'f')) {
                    if (ch_pp1 == 'a') {
                        // Skip spaces between a and href
                        endPos = pos + 2;
                        while (endPos < str.length && str[endPos] == ' ')
                            ++endPos;
                    } else {
                        endPos = pos + 1;
                    }
                    if (str.length - endPos > 3) {
                        ch_pp1 = Character.toLowerCase(str[endPos]);
                        char ch_pp2 = Character.toLowerCase(str[endPos + 1]);
                        char ch_pp3 = Character.toLowerCase(str[endPos + 2]);
                        char ch_pp4 = Character.toLowerCase(str[endPos + 3]);
                        if ((ch_pp1 == 'h' && ch_pp2 == 'r' && ch_pp3 == 'e' && ch_pp4 == 'f')
                                || (ch_pp1 == 'f' && ch_pp2 == 'o' && ch_pp3 == 'n' && ch_pp4 == 't')
                                || (ch_pp1 == 't' && ch_pp2 == 'a' && ch_pp3 == 'r' && ch_pp4 == 'g')
                                || (ch_pp1 == 't' && ch_pp2 == 'i' && ch_pp3 == 't' && ch_pp4 == 'l')
                                || (ch_pp1 == 'r' && ch_pp2 == 'e' && ch_pp3 == 'l')) {
                            // Ignore everything, including newline, till next
                            // '>'
                            while (endPos < str.length && str[endPos] != '>')
                                ++endPos;
                        }
                    }
                    linebreak[LBI_BREAK] = pos - 1;
                    if (endPos + 1 < str.length)
                        linebreak[LBI_NEXT] = endPos + 1;
                    else
                        linebreak[LBI_NEXT] = str.length - 1;
                    linebreak[LBI_TYPE] = LB_SKIP;
                    return endPos + 1;
                }
                // XXX FIXME: Ignore <p class=...> & <img src=...>,<span ...>
                // <lp...> , </lp:...>, <table > <div>here. We will capture the
                // hyper link
                // and tie it to the link body later.
                if (str.length - pos >= 8
                        && (ch_pp1 == 'p' || ch_pp1 == 'i' || ch_pp1 == 'l' || ch_pp1 == 's' || ch_pp1 == 't' || ch_pp1 == 'd')
                        || ch_pp1 == '/') {
                    endPos = pos + 2;
                    // Ignore everything, including newline, till next '>'
                    while (endPos < str.length && str[endPos] != '>')
                        ++endPos;


                    linebreak[LBI_BREAK] = pos - 1;
                    linebreak[LBI_NEXT] = endPos + 1;
                    linebreak[LBI_TYPE] = LB_SKIP;
                    return endPos + 1;
                }


                // For tags that we do not recognize, fall through.
            }
            case ' ':
            case '\t':
                lastBreakPoint = pos;
                // fall through
            default:
                // Check one more character to see if it fits current string. If
                // not,
                // try to break at the last line break point. If that is too far
                // away,
                // add a hyphen at this place.
                strWidth += f.measureText(String.valueOf(str[pos]));
                if (strWidth >= maxLineWidth) {
                    // This was meant for hyphenation. When we insert a newline,
                    // can do that right at the starting point.
                    if (lastBreakPoint == startPos) {
                        lastBreakPoint = (pos == startPos) ? pos : (pos - 1);
                    }
                    linebreak[LBI_BREAK] = lastBreakPoint - 1;
                    linebreak[LBI_NEXT] = lastBreakPoint;
                    linebreak[LBI_TYPE] = LB_NEWLINE;
                    return lastBreakPoint;
                }
            }
            ++pos;
        }
        return -1;
    }


    /**
     * Starting from pos in str, find the longest string that fits in maxLineWidth.
     * 
     * Don't need to check
     * <p>
     * , <br>
     * ,
     * <p/>
     * , <br/>
     * ,
     * </p>
     * , </br>, <b>
     * <tr>
     * <td>again. They should be parsed by HTML.
     */
    public final static int getLineEndIndex(char[] chars, int pos, Paint f, int LineWidth, int[] linebreak,
            boolean newLine, boolean isTrimHeadBlank, boolean isBreakWord) {
        if (chars == null || f == null)
            return -1;
        int strWidth = 0;
        int startPos = pos;
        int lastBreakPoint = pos;
        // Filter out initial white spaces at start of a line.
        if (newLine) {
            if (isTrimHeadBlank) {
                while (pos < chars.length && (chars[pos] == ' ' || chars[pos] == '\t'))
                    ++pos;
            } else {
                while (pos < chars.length && chars[pos] == '\t')
                    ++pos;
            }
        }
        linebreak[LBI_START] = pos;
        while (pos < chars.length) {
            switch (chars[pos]) {
            // NOTE: don't recognize CR because in XML, only <br> or <p> should
            // matter.
            case '\n':
                chars[pos] = ' ';
                return pos;
            case ' ':
            case '.':
            case '/':
            case '\t':
                lastBreakPoint = pos + 1;
            default:
                // Check one more character to see if it fits current string. If
                // not,
                // try to break at the last line break point.
                strWidth += f.measureText(String.valueOf(chars[pos]));
                if (strWidth > LineWidth) {
                    if (isBreakWord) {
                        return pos - 1;
                    } else {
                        // It is possible that a word's width bigger than line
                        // width.
                        if (lastBreakPoint == startPos && pos > startPos && newLine) {
                            return pos - 1;
                        } else {
                            return lastBreakPoint;
                        }
                    }
                }
            }
            ++pos;
        }
        return chars.length;
    }


    public final static boolean isPunct(char ch) {
        switch (ch) {
        case '.':
        case ',':
        case '?':
        case '!':
        case '\'':
        case '"':
        case ':':
        case ';':
        case '-':
            return true;
        }
        return false;
    }


    public final static String escapeHTML(String str) {
        if (str == null)
            return null;
        StringBuffer sb = new StringBuffer();
        int num = str.length();
        for (int i = 0; i < num; i++) {
            char ch = str.charAt(i);
            switch (ch) {
            case '<':
                sb.append("&lt;");
                break;
            case '>':
                sb.append("&gt;");
                break;
            case '&':
                sb.append("&amp;");
                break;
            case '"':
                sb.append("&quot;");
                break;
            case ' ':
                sb.append("&nbsp;");
                break;
            default:
                sb.append(ch);
                break;
            }
        }
        return sb.toString();
    }


    /**
     * Unescape a HTML string by filtering out &...; sequences, including &#<numbers>;. We do not consider the case
     * where one sequence is broken into multiple lines.
     */
    public final static String unescapeHTML(String str) {
        char[] src = str.toCharArray();
        char[] dest = new char[src.length];
        boolean foundAmber = false;
        int escapeIdx = -1;
        int dstIdx = 0;
        int j;
        int ch;


        for (int i = 0; i < src.length; i++) {
            switch (src[i]) {
            case '&':
                // we should not miss the case &aaa&.
                while (escapeIdx > 0 && escapeIdx < i) {
                    dest[dstIdx++] = src[escapeIdx++];
                }
                escapeIdx = i;
                break;
            case ';':
                if (escapeIdx >= 0) {
                    if (src[escapeIdx + 1] == '#') {
                        ch = 0;
                        for (j = escapeIdx + 2; j < i; ++j) {
                            if (src[j] < '0' || src[j] > '9') {
                                ch = -1;
                                break;
                            } else {
                                ch *= 10;
                                ch += src[j] - '0';
                            }
                        }
                    } else {
                        int pp1 = escapeIdx + 1;
                        int pp2 = escapeIdx + 2;
                        int pp3 = escapeIdx + 3;
                        int pp4 = escapeIdx + 4;
                        int pp5 = escapeIdx + 5;
                        if (src[pp1] == 'q' && src[pp2] == 'u' && src[pp3] == 'o' && src[pp4] == 't' && pp5 == i)
                            ch = '"';
                        else if (src[pp1] == 'a' && src[pp2] == 'm' && src[pp3] == 'p' && pp4 == i)
                            ch = '&';
                        else if (src[pp1] == 'l' && src[pp2] == 't' && pp3 == i)
                            ch = '<';
                        else if (src[pp1] == 'g' && src[pp2] == 't' && pp3 == i)
                            ch = '>';
                        else if (src[pp1] == 'n' && src[pp2] == 'b' && src[pp3] == 's' && src[pp4] == 'p' && pp5 == i)
                            ch = ' ';
                        else
                            ch = -1;
                        if (ch != -1) {
                            dest[dstIdx++] = (char) ch;
                            escapeIdx = -1;
                            break;
                        }
                    }
                } else
                    ch = -1;


                // Replace the Html Special Characters likes &#<numbers>.
                if (ch >= 0) {
                    switch (ch) {
                    case 123:
                        ch = '{';
                        break;
                    case 125:
                        ch = '}';
                        break;
                    case 133:
                        for (int k = 0; k < 3; k++)
                            dest[dstIdx++] = '.';
                        ch = -1;
                        break;
                    case 146:
                        ch = '\'';
                        break;
                    case 147:
                    case 148:
                        ch = '"';
                        break;
                    case 151:
                        ch = '-';
                        break;
                    default:
                        ch = ' ';
                        break;
                    }
                    if (ch != -1)
                        dest[dstIdx++] = (char) ch;
                } else {
                    // If current charactor is only a characters ";" ,
                    // not a Html Special Characters, we copy it in dest.
                    if (escapeIdx < 0)
                        dest[dstIdx++] = src[i];
                    else {
                        for (j = escapeIdx; j <= i; ++j)
                            dest[dstIdx++] = src[j];
                    }
                }
                escapeIdx = -1;
                break;
            default:
                if (escapeIdx < 0)
                    dest[dstIdx++] = src[i];
                else if (escapeIdx > 0 && ((i - escapeIdx) > 5)) {
                    for (int k = escapeIdx; k <= i; k++) {
                        dest[dstIdx++] = src[k];
                    }
                    escapeIdx = -1;
                }
                break;
            }
        }


        return String.valueOf(dest, 0, dstIdx);
    }


    /**
     * non-ASCII characters are encoded as: first using the UTF-8 algorithm to encode to a sequence of 2 or 3 bytes,
     * then each of these bytes is encoded as "%xx".
     */
    public final static String escapeURIComponent(String str) {
        printOutToConsole("escapeURIComponent  " + str);
        if (str == null)
            return null;
        StringBuffer sbuf = new StringBuffer();
        int ch;
        int len = str.length();
        for (int i = 0; i < len; i++) {
            ch = str.charAt(i);
            if ('A' <= ch && ch <= 'Z') {
                sbuf.append((char) ch);
            } else if ('a' <= ch && ch <= 'z') {
                sbuf.append((char) ch);
            } else if ('0' <= ch && ch <= '9') {
                sbuf.append((char) ch);
            } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\''
                    || ch == '(' || ch == ')') {
                sbuf.append((char) ch);
            } else if (ch < 0x0F) {
                sbuf.append('%');
                sbuf.append('0');
                sbuf.append(Integer.toHexString(ch));
            } else if (ch < 0x7F) {
                sbuf.append('%');
                sbuf.append(Integer.toHexString(ch));
            } else if (ch <= 0x07FF) { // non-ASCII characters and value <=
                // 0x7FF
                sbuf.append('%');
                sbuf.append(Integer.toHexString(0xc0 | (ch >> 6)));
                sbuf.append('%');
                sbuf.append(Integer.toHexString(0x80 | (ch & 0x3F)));
            } else { // non-ASCII characters and value <= 0xFFFF
                sbuf.append('%');
                sbuf.append(Integer.toHexString(0xe0 | (ch >> 12)));
                sbuf.append('%');
                sbuf.append(Integer.toHexString(0x80 | ((ch >> 6) & 0x3F)));
                sbuf.append('%');
                sbuf.append(Integer.toHexString(0x80 | (ch & 0x3F)));
            }
        }
        return sbuf.toString();
    }


    public final static String unescapeURIComponent(String url) {
        if (url == null)
            return null;
        StringBuffer sbuf = new StringBuffer();
        int len = url.length();
        int ch;
        int b;
        int total = 0;
        int more = -1;
        int hb;
        int lb;
        for (int i = 0; i < len; i++) {
            switch (ch = url.charAt(i)) {
            case '%':
                ch = url.charAt(++i);
                hb = (Character.isDigit((char) ch) ? ch - '0' : 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
                ch = url.charAt(++i);
                lb = (Character.isDigit((char) ch) ? ch - '0' : 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
                b = (hb << 4) | lb;
                break;
            default:
                b = ch;
            }
            // Decode byte b as UTF-8, sumb collects incomplete chars
            if ((b & 0xc0) == 0x80) {
                total = (total << 6) | (b & 0x3f);
                if (--more == 0)
                    sbuf.append((char) total);
            } else if ((b & 0x80) == 0x00) {
                sbuf.append((char) b);
            } else if ((b & 0xe0) == 0xc0) {
                total = b & 0x1f;
                more = 1;
            } else if ((b & 0xf0) == 0xe0) {
                total = b & 0x0f;
                more = 2;
            } else if ((b & 0xf8) == 0xf0) {
                total = b & 0x07;
                more = 3;
            } else if ((b & 0xfc) == 0xf8) {
                total = b & 0x03;
                more = 4;
            } else {
                total = b & 0x01;
                more = 5;
            }
        }
        return sbuf.toString();
    }


    /**
     * Convert a string of double number to an integer, assuming the number is a coordinate.
     */
    public final static int strCoordToInt(String strDouble) {
        char[] src = strDouble.toCharArray();
        StringBuffer dst = new StringBuffer();
        int digitsAfterDot = 0;
        boolean dotFound = false;
        for (int i = 0; i < src.length && digitsAfterDot < 6; i++) {
            if (src[i] != '.') {
                dst.append(src[i]);
                if (dotFound) {
                    ++digitsAfterDot;
                }
            } else {
                dotFound = true;
            }
        }
        // multiply by 1000000
        for (int i = digitsAfterDot; i < 6; i++) {
            dst.append('0');
        }
        return Integer.parseInt(dst.toString());
    }


    /**
     * Convert an integer coordinate into a float string.
     */
    public final static String intToFloatCoord(int coord) {
        StringBuffer buf = new StringBuffer();
        buf.append(coord);
        buf.insert(buf.length() - 6, '.');
        return buf.toString();
    }


    /**
     * Parse a ISO8601 date string and return it in a calendar object. Return UTC time.
     */
    public final static long parseISO8601(String text) throws Exception {
        printOutToConsole(text);


        // Example date string: 2006-11-29T15:30:00.000-08:00
        int idx = text.indexOf('T');
        if (idx == -1) {
            // Invalid date
            throw new IOException("錯誤的資料格?");
        }
        String date = text.substring(0, idx);
        int idx2 = text.indexOf('-', ++idx);
        String time;
        if (idx2 == -1) {
            time = text.substring(idx);
        } else {
            time = text.substring(idx, idx2);
        }


        idx = date.indexOf('-');
        int year = Integer.parseInt(date.substring(0, idx));
        idx2 = date.indexOf('-', ++idx);
        int month = Integer.parseInt(date.substring(idx, idx2));
        int day = Integer.parseInt(date.substring(++idx2));


        idx = time.indexOf(':');
        int hour = Integer.parseInt(time.substring(0, idx));
        idx2 = time.indexOf(':', ++idx);
        int min = Integer.parseInt(time.substring(idx, idx2));
        // The separater between seconds and time offset field can be 'Z' for
        // standard time,
        // or +/- for time zone offset.
        int idx3 = time.indexOf('+', ++idx2);
        String zoneString = null;
        int zoneOffset = 0; // time zone offset, in seconds
        if (idx3 == -1) {
            idx3 = time.indexOf('-', idx2);
        } else {
            zoneString = time.substring(idx3 + 1);
            zoneOffset = 1;
        }
        if (idx3 == -1) {
            idx3 = time.indexOf('Z', idx2);
        } else {
            zoneString = time.substring(idx3 + 1);
            zoneOffset = -1;
        }
        if (idx3 == -1) {
            idx3 = time.length();
        } else if (zoneString != null) {
            int idx4 = zoneString.indexOf(':', 0);
            if (idx4 != -1) {
                int zoneHour;
                int zoneMin;
                try {
                    zoneHour = Integer.parseInt(zoneString.substring(0, idx4));
                    zoneMin = Integer.parseInt(zoneString.substring(idx4 + 1));
                    zoneOffset *= zoneHour * 3600 + zoneMin * 60;
                } catch (Exception ignored) {
                    zoneOffset = 0;
                }
            }
        }
        int sec = 0;
        if (idx3 > idx2) {
            // Filter out sub-second component
            int idx4 = time.indexOf('.', idx2);
            if (idx4 != -1)
                idx3 = idx4;
            sec = Integer.parseInt(time.substring(idx2, idx3));
        }


        // Make a calendar time assuming that is UTC, then we'll make time zone
        // adjustment from UTC seconds.
        Calendar updatedDate = Calendar.getInstance();
        updatedDate.set(Calendar.YEAR, year);
        updatedDate.set(Calendar.MONTH, month - 1); // NOTE: month starts from
        // 0??
        updatedDate.set(Calendar.DAY_OF_MONTH, day); // Day start from 1??
        updatedDate.set(Calendar.HOUR_OF_DAY, hour);
        updatedDate.set(Calendar.MINUTE, min);
        updatedDate.set(Calendar.SECOND, sec);
        updatedDate.set(Calendar.MILLISECOND, 0);
        printOutToConsole("date: " + year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec + " time "
                + updatedDate.getTime());
        printOutToConsole("timezone offset " + zoneOffset);
        return updatedDate.getTime().getTime() + zoneOffset * 1000;
    }


    /**
     * Displays a UTC time (in milliseconds) as MM/DD-HH:MM
     */
    public final static void formatTimeString(long utc, StringBuffer str) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(utc));
        str.append(cal.get(Calendar.MONTH) + 1); // NOTE: month starts from 0
        str.append("/");
        str.append(cal.get(Calendar.DAY_OF_MONTH));
        str.append("-");
        str.append(cal.get(Calendar.HOUR_OF_DAY));
        str.append(":");
        str.append(cal.get(Calendar.MINUTE));
    }


    /**
     * Displays a UTC time in milliseconds as YYYY/MM/DD
     */
    public final static void formatDateString(long utc, StringBuffer str) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(utc));
        str.append(cal.get(Calendar.YEAR));
        str.append("/");
        str.append(cal.get(Calendar.MONTH) + 1); // NOTE: month starts from 0
        str.append("/");
        str.append(cal.get(Calendar.DAY_OF_MONTH));
    }


    /**
     * Display a UTC time as format hh:mm am/pm
     */
    public final static void fromatTimeAmPm(long utc, StringBuffer str) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(utc));
        int hour = cal.get(Calendar.HOUR);
        int minu = cal.get(Calendar.MINUTE);
        if (hour < 10)
            str.append("0");
        str.append(hour);
        str.append(":");
        if (minu < 10)
            str.append("0");
        str.append(minu);
        if (cal.get(Calendar.AM_PM) == 0)
            str.append(" AM");
        else
            str.append(" PM");
    }


    /**
     * Parse simple state response XMLs from server and retrieve attributes from it. Used by UserManager, PoiRepository
     * (add/remove POI), etc.
     */
    public final static String getXMLResponseAttribute(String reply, String attr, int startIndex, char matchCh) {
        int start = reply.indexOf(attr, startIndex);
        if (start < 0)
            return null;
        start += attr.length();
        int end = reply.indexOf(matchCh, start);
        if (end > start) {
            String s = reply.substring(start, end);
            return s;
        }
        return null;
    }


    /**
     * Check the email address whether is valid.
     */
    public final static boolean checkEmail(String emailAdd) {
        int length = emailAdd.length();
        int temp1 = emailAdd.indexOf("@");
        int temp2 = emailAdd.indexOf(".");


        // temp1 > 1 means in this email address contains an “@?and there is a
        // user’s name before it.
        // temp2 != -1 means in this email address contains an "."
        // (length - temp1 > 3) means after “@?contains at least one letter
        // except ??
        // (length - temp2 > 1) means "." is not the last letter
        if (temp1 > 1 && temp2 != -1 && (length - temp1 > 3) && (length - temp2 > 1)) {
            return true;
        } else
            return false;
    }


    /*
     * A Java implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS PUB 180-1 Copyright (C) Sam Ruby
     * 2004 All rights reserved
     * 
     * Based on code Copyright (C) Paul Johnston 2000 - 2002. See http://pajhome.org.uk/site/legal.html for details.
     * 
     * Converted to Java by Russell Beattie 2004 Base64 logic and inlining by Sam Ruby 2004 Bug fix correcting single
     * bit error in base64 code by John Wilson
     * 
     * BSD License
     * 
     * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
     * following conditions are met:
     * 
     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
     * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
     * the following disclaimer in the documentation and/or other materials provided with the distribution.
     * 
     * Neither the name of the author nor the names of its contributors may be used to endorse or promote products
     * derived from this software without specific prior written permission.
     * 
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */


    // /*
    // * Bitwise rotate a 32-bit number to the left
    // */
    // private final final static int rol(int num, int cnt) {
    // return (num << cnt) | (num >>> (32 - cnt));
    // }
    /*
     * Take a string and return the base64 representation of its SHA-1.
     */
    public final static String sha1(String str) throws Exception {


        // Convert a string to a sequence of 16-word blocks, stored as an array.
        // Append padding bits and the length, as described in the SHA1 standard


        byte[] x = str.getBytes("UTF-8");
        int[] blks = new int[(((x.length + 8) >> 6) + 1) * 16];
        int i;


        for (i = 0; i < x.length; i++) {
            blks[i >> 2] |= x[i] << (24 - (i % 4) * 8);
        }


        blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8);
        blks[blks.length - 1] = x.length * 8;


        // calculate 160 bit SHA1 hash of the sequence of blocks


        int[] w = new int[80];


        int a = 1732584193;
        int b = -271733879;
        int c = -1732584194;
        int d = 271733878;
        int e = -1009589776;
        int num;


        for (i = 0; i < blks.length; i += 16) {
            int olda = a;
            int oldb = b;
            int oldc = c;
            int oldd = d;
            int olde = e;


            for (int j = 0; j < 80; j++) {
                if (j < 16) {
                    w[j] = blks[i + j];
                } else {
                    num = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
                    w[j] = ((num << 1) | (num >>> (32 - 1)));
                }
                int t = ((a << 5) | (a >>> (32 - 5)))
                        + e
                        + w[j]
                        + ((j < 20) ? 1518500249 + ((b & c) | ((~b) & d)) : (j < 40) ? 1859775393 + (b ^ c ^ d)
                                : (j < 60) ? -1894007588 + ((b & c) | (b & d) | (c & d)) : -899497514 + (b ^ c ^ d));
                e = d;
                d = c;
                c = ((b << 30) | (b >>> (32 - 30))); // rol(b, 30);
                b = a;
                a = t;
            }


            a = a + olda;
            b = b + oldb;
            c = c + oldc;
            d = d + oldd;
            e = e + olde;
        }


        // Convert 160 bit hash to base64
        int[] words = { a, b, c, d, e, 0 };
        byte[] base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes();
        byte[] result = new byte[28];
        for (i = 0; i < 27; i++) {
            int start = i * 6;
            int word = start >> 5;
            int offset = start & 0x1f;


            if (offset <= 26) {
                result[i] = base64[(words[word] >> (26 - offset)) & 0x3F];
            } else if (offset == 28) {
                result[i] = base64[(((words[word] & 0x0F) << 2) | ((words[word + 1] >> 30) & 0x03)) & 0x3F];
            } else {
                result[i] = base64[(((words[word] & 0x03) << 4) | ((words[word + 1] >> 28) & 0x0F)) & 0x3F];
            }
        }
        result[27] = '=';


        return new String(result);
    }


    public final static Bitmap bilinearResample(Bitmap oriImg, int newWidth, int newHeight) {
        return bilinearResample(oriImg, newWidth, newHeight, WHITE);
    }


    /**
     * Function for scale an image from a large to a smaller size, then return a new image with new size predefined.
     */
    public final static Bitmap bilinearResample(Bitmap oriImg, int newWidth, int newHeight, int bgColor) {
        int oldW = oriImg.getWidth();
        int oldH = oriImg.getHeight();


        int[] ori_rgb = new int[oldW * oldH];
        int[] des_rgb = new int[newWidth * newHeight];


        oriImg.getPixels(ori_rgb, 0, oldW, 0, 0, oldW, oldH);


        int newIndex = 0;
        // draw each pixel in the new image
        for (int i = 1; i <= newHeight; i++) {
            // generate the y calculation variables
            int interplY = (i - 1) * oldH / newHeight;
            int calcY = (i - 1) * oldH % newHeight;


            for (int j = 1; j <= newWidth; j++) {
                // generate the x calculation variables
                int interplX = (j - 1) * oldW / newWidth;
                int calcX = (j - 1) * oldW % newWidth;


                int theColour1 = ori_rgb[(interplX) + (interplY) * oldW];
                int theColour2 = ori_rgb[(interplX + 1) + (interplY) * oldW];
                int theColour3 = ori_rgb[(interplX) + (interplY + 1) * oldW];
                int theColour4 = ori_rgb[(interplX + 1) + (interplY + 1) * oldW];


                // calculate the new colour
                int alpha1 = (theColour1 >> 24 & 0xFF) * (newHeight - calcY) / newHeight + (theColour3 >> 24 & 0xFF)
                        * calcY / newHeight;
                int alpha2 = (theColour2 >> 24 & 0xFF) * (newHeight - calcY) / newHeight + (theColour4 >> 24 & 0xFF)
                        * calcY / newHeight;
                int newAlpha = isOutofColor(alpha1 * (newWidth - calcX) / newWidth + alpha2 * calcX / newWidth);
                int newColor1r = (theColour1 >> 16 & 0xFF) * (newHeight - calcY) / newHeight
                        + (theColour3 >> 16 & 0xFF) * calcY / newHeight;
                int newColor2r = (theColour2 >> 16 & 0xFF) * (newHeight - calcY) / newHeight
                        + (theColour4 >> 16 & 0xFF) * calcY / newHeight;
                int newColorr = isOutofColor(newColor1r * (newWidth - calcX) / newWidth + newColor2r * calcX / newWidth);
                int newColor1g = (theColour1 >> 8 & 0xFF) * (newHeight - calcY) / newHeight + (theColour3 >> 8 & 0xFF)
                        * calcY / newHeight;
                int newColor2g = (theColour2 >> 8 & 0xFF) * (newHeight - calcY) / newHeight + (theColour4 >> 8 & 0xFF)
                        * calcY / newHeight;
                int newColorg = isOutofColor(newColor1g * (newWidth - calcX) / newWidth + newColor2g * calcX / newWidth);
                int newColor1b = (theColour1 & 0xFF) * (newHeight - calcY) / newHeight + (theColour3 & 0xFF) * calcY
                        / newHeight;
                int newColor2b = (theColour2 & 0xFF) * (newHeight - calcY) / newHeight + (theColour4 & 0xFF) * calcY
                        / newHeight;
                int newColorb = isOutofColor(newColor1b * (newWidth - calcX) / newWidth + newColor2b * calcX / newWidth);
                int newColor = ((newAlpha & 0xFF) << 24) | ((newColorr & 0xFF) << 16) | ((newColorg & 0xFF) << 8)
                        | (newColorb & 0xFF);


                // Set this pixel into the new image
                des_rgb[newIndex++] = newColor;
            }
        }


        Bitmap desImage = null;
        desImage = Bitmap.createBitmap(des_rgb, newWidth, newHeight, Bitmap.Config.RGB_565);
        // 縮放圖片
        desImage = Bitmap.createScaledBitmap(desImage, newWidth, newHeight, true);
        if (desImage == null)
            desImage = oriImg;
        return desImage;
    }


    // it's used by the function bilinearResample().
    final static int isOutofColor(int color) {
        if (color < 0)
            color = 0;
        if (color > 255)
            color = 255;
        return color;
    }


    /**
     * If we are on low memory or the photo can not be loaded, we will show a red cross in the photo frame.
     */
    final static void createImageLoadFail(Canvas canvas) {
        Paint paint = new Paint();
        paint.setTypeface(Typeface.DEFAULT);
        int wh = (int) (4 * paint.getTextSize());
        canvas.drawRGB(0, 0, 0);
        canvas.drawRect(new Rect(0, 0, wh - 1, wh - 1), new Paint());
        canvas.drawRGB(255, 0, 0);
        int xStart = wh / 2 - 3;
        int yStart = wh / 2 - 3;


        canvas.drawLine(xStart, yStart, xStart + 6, yStart + 6, paint);
        canvas.drawLine(xStart, yStart + 1, xStart + 6, yStart + 7, paint);
        canvas.drawLine(xStart + 6, yStart, xStart, yStart + 6, paint);
        canvas.drawLine(xStart + 6, yStart + 1, xStart, yStart + 7, paint);
    }


    final static boolean isInComponent(int x, int y, View c) {
        if (c == null)
            return false;
        if (x < c.getLeft() || x > c.getLeft() + c.getWidth() || y < c.getTop() || y > c.getTop() + c.getHeight())
            return false;
        else
            return true;
    }


    /**
     * Divide the given string to paragraphs with the given font and width.
     * 
     * @param strSource
     *            the source string
     * @param font
     *            the font
     * @param width
     *            the width of each line.
     * @return the String array
     */
    public final static String[] getParagraph(final String strSource, final Paint font, final int width) {
        String[] strs = null;
        if (strSource == null || font == null || width <= 0) {
            return strs;
        } else if (strSource.equals("")) {
            strs = new String[] { "" };
            return strs;
        }
        Vector<String> vector = new Vector<String>();
        try {
            String temp = strSource;
            int i, j;
            boolean isOver;
            while (!temp.equals("")) {
                isOver = false;
                i = temp.indexOf("\n");
                if (i == -1) {
                    i = temp.length();
                    isOver = font.measureText(temp) > width;
                }
                if (i > 0 && font.measureText(temp.substring(0, i)) > width) {
                    isOver = true;
                }
                if (isOver) {
                    // gets the critical point
                    while (font.measureText(temp.substring(0, i)) > width) {
                        i = i - 1;
                    }
                }
                vector.addElement(temp.substring(0, i));
                if (i == temp.length()) {
                    temp = "";
                } else {
                    if (temp.charAt(i) == '\n') {
                        temp = temp.substring(i + 1);
                    } else {
                        temp = temp.substring(i);
                    }
                }
            }
            if (vector.size() > 0) {
                strs = new String[vector.size()];
                vector.copyInto(strs);
            }
        } catch (Exception e) {


            printOutToConsole("getSubsection:" + e);
        }
        return strs;
    }


    /**
     * Divide the given string to paragraphs with the given font and width.
     * 
     * @param strSource
     *            the source string
     * @param font
     *            the font
     * @param width
     *            the width of each line.
     * @return the String array
     */
    public final static String[] getParagraph(final String strSource, final Paint font, final int width,
            final String splitChars) {
        String[] strs = null;
        if (strSource == null || font == null || width <= 0) {
            return strs;
        } else if (strSource.equals("")) {
            strs = new String[] { "" };
            return strs;
        }
        Vector vector = new Vector(2);
        try {
            String temp = strSource;
            int i, j;
            boolean isOver;
            while (!temp.equals("")) {
                isOver = false;
                i = temp.indexOf("\n");
                if (i == -1) {
                    i = temp.length();
                    isOver = font.measureText(temp) > width;
                }
                if (i > 0 && font.measureText(temp.substring(0, i)) > width) {
                    isOver = true;
                }
                if (isOver) {
                    // gets the critical point
                    while (font.measureText(temp.substring(0, i)) > width) {
                        i = i - 1;
                    }
                    if (!splitChars.equals("")) {
                        j = i; // restore the last index of this line for maybe
                        // there is a long word over a line.
                        while (splitChars.indexOf(temp.charAt(i - 1)) == -1) {
                            i--;
                            if (i == 0) {
                                i = j; // crash a word, if it is needed.
                                break;
                            }
                        }
                    }
                }
                vector.addElement(temp.substring(0, i));
                if (i == temp.length()) {
                    temp = "";
                } else {
                    if (temp.charAt(i) == '\n') {
                        temp = temp.substring(i + 1);
                    } else {
                        temp = temp.substring(i);
                    }
                }
            }
            if (vector.size() > 0) {
                strs = new String[vector.size()];
                vector.copyInto(strs);
            }
        } catch (Exception e) {


            printOutToConsole("getSubsection:" + e);
        }
        return strs;
    }


    /**
     * @param src
     *            source image.
     * @param desW
     *            expected width
     * @param desH
     *            expected height
     * @param isBackgroundTrans
     *            makes the background transparent
     * @param isTrans
     *            makes the image transparent
     * @return Image
     */
    // public final final static final Image scaleImage(Image src, int desW, int desH,
    // boolean isBackgroundTrans, boolean isTrans) {
    // Image desImg = null;
    // try {
    // int srcW = src.getWidth(); // source image width
    // int srcH = src.getHeight(); // source image height
    // int[] srcBuf = new int[srcW * srcH]; // source image pixel
    // src.getRGB(srcBuf, 0, srcW, 0, 0, srcW, srcH);
    // // compute interpolation table
    // int[] tabY = new int[desH];
    // int[] tabX = new int[desW];
    // int sb = 0;
    // int db = 0;
    // int tems = 0;
    // int temd = 0;
    // int distance = srcH > desH ? srcH : desH;
    // for (int i = 0; i <= distance; i++) { /* vertical direction */
    // tabY[db] = sb;
    // tems += srcH;
    // temd += desH;
    // if (tems > distance) {
    // tems -= distance;
    // sb++;
    // }
    // if (temd > distance) {
    // temd -= distance;
    // db++;
    // }
    // }
    // sb = 0;
    // db = 0;
    // tems = 0;
    // temd = 0;
    // distance = srcW > desW ? srcW : desW;
    // for (int i = 0; i <= distance; i++) { /* horizontal direction */
    // tabX[db] = (short) sb;
    // tems += srcW;
    // temd += desW;
    // if (tems > distance) {
    // tems -= distance;
    // sb++;
    // }
    // if (temd > distance) {
    // temd -= distance;
    // db++;
    // }
    // }
    // // formation enlarge and shorten buffer pixel
    // int[] desBuf = new int[desW * desH];
    // int dx = 0;
    // int dy = 0;
    // int sy = 0;
    // int oldy = -1;
    // for (int i = 0; i < desH; i++) {
    // if (oldy == tabY[i]) {
    // System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);
    // } else {
    // dx = 0;
    // for (int j = 0; j < desW; j++) {
    // desBuf[dy + dx] = srcBuf[sy + tabX[j]];
    // dx++;
    // }
    // sy += (tabY[i] - oldy) * srcW;
    // }
    // oldy = tabY[i];
    // dy += desW;
    // }
    // if (isTrans) {
    // // int a= 100;//set the transparence of pixel 100
    // for (int i = 0; i < desBuf.length; i++) {
    // if (desBuf[i] == 0x00FFFFFF)
    // continue;
    // int alpha = ((desBuf[i] & 0xff000000) >>> 24) == 0 ? 0 : 100;
    // desBuf[i] = ((alpha + 1) << 24) | (desBuf[i] & 0x00FFFFFF);
    // }
    // }
    // if (isBackgroundTrans) {
    // desImg = Image.createRGBImage(desBuf, desW, desH, true);
    // } else {
    // desImg = Image.createRGBImage(desBuf, desW, desH, false);
    // }
    // } catch (Exception ex) {
    // LPUtils.printException(ex);
    // desImg = null;
    // }
    // return desImg;
    // }


    /**
     * @param strSrc
     * @param strSepr
     * @return
     */
    public final static String[] string2Array(String strSrc, String strSepr) {
        String[] astr = null;
        try {
            Vector vec = new Vector(5, 1);
            str2Arr(strSrc, strSepr, vec);
            vec.trimToSize();
            astr = new String[vec.size()];
            vec.copyInto(astr);
            vec.removeAllElements();
        } catch (Exception ex) {
        }
        return astr;
    }


    public final static void setPrint(Activity activity) {
        if (getConfigStringFormAsset(activity, "isprintMessage").equalsIgnoreCase("true")) {
            isPrintMessage_ = true;
        }
        setActivity(activity);
    }


    public final static void setActivity(Activity activity) {
        // TODO Auto-generated method stub
        activity_ = activity;
    }


    public final static Activity getActivity() {
        return activity_;
    }


    private final static void str2Arr(String strSrc, String strSepr, Vector vec) throws Exception {
        if (strSrc.indexOf(strSepr) == -1) {
            vec.addElement(strSrc);
        } else {
            vec.addElement(strSrc.substring(0, strSrc.indexOf(strSepr)));
            strSrc = strSrc.substring(strSrc.indexOf(strSepr) + strSepr.length());
            str2Arr(strSrc, strSepr, vec);
        }
    }


    // 將傳入的字串中的特殊字元予以替換,保證解析順利進行或使已替換的字串還?
    public final static String insteadOfSpecillCharacter(String str, boolean isReplace) {
        if ((null == str) || (str.equalsIgnoreCase("")))
            return str;
        String strTemp = str;
        // 特殊字元?
        String[] A = { "&" };
        // 需要替換的字元
        String[] substitute;
        // 替換後的字元?
        String[] B = { "***###" };
        // 替換後的字元
        String[] beReplaced;


        if (isReplace) {
            // 替換特殊字串,方便解析
            substitute = A;
            beReplaced = B;
        } else {
            // 將特殊字元還?
            substitute = B;
            beReplaced = A;
        }
        int length = substitute.length;
        for (int i = 0; i < length; i++) {
            strTemp = strTemp.replace(substitute[i], beReplaced[i]);
        }
        return strTemp;
    }


    // 設定與是否獲得焦?
    // final final static public void setFocus(boolean isFocus, int keyCode, View view) {
    // if (view instanceof LPTable) {
    // LPTable temp = (LPTable) view;
    // // temp.setFocus(isFocus, keyCode);
    // } else {
    // if (isFocus) {
    // view.requestFocus();
    // } else {
    // view.clearFocus();
    // }
    // }
    //
    // }


    /**
     * @param obj
     */
    public final static void printOutToConsole(Object obj) {
        if (isPrintMessage_) {
            if (obj instanceof String) {
                printOutToConsole((String) obj);
            } else if (obj instanceof byte[]) {
                printOutToConsole((byte[]) obj);
            } else {
                System.out.println(obj);
            }
        }
    }


    /**
     * @param s
     */
    public final static void printOutToConsole(String s) {
        if (isPrintMessage_) {
            int length = s.length();
            int offset = 3000;
            if (length > offset) {// 解決報文過長,列印不全的問題?
                int n = 0;
                for (int i = 0; i < length; i += offset) {
                    n += offset;
                    if (n > length)
                        n = length;
                    System.err.println("Debug = " + s.substring(i, n));
                }
            } else {
                System.err.println("Debug = " + s);
            }
        }
    }


    /**
     * @param byts
     */
    public final static void printOutToConsole(byte[] byts) {
        if (isPrintMessage_) {
            if (byts == null) {
                return;
            }
            for (int i = 0; i < byts.length; i++) {
                System.out.print("[" + i + "]" + " : \t");
                System.out.println(byts[i]);
            }
        }
    }


    public final static void LogD(String tag, String msg) {
        if (isPrintMessage_) {
            android.util.Log.d(tag, msg);
        }
    }


    public final static void printException(Exception e) {
        if (isPrintMessage_) {
            e.printStackTrace();
        }
    }


    /**
     * 檢測服務端發過來的介面前面是否帶有/,如果沒有則自動加?提高程式健壯?
     * 
     * @param url
     * @return
     */
    public final static String checkUrl(Object url) {
        // TODO Auto-generated method stub
        String tempString = url.toString().trim();
        if (!tempString.startsWith("/"))
            tempString = "/".concat(tempString);
        return tempString;
    }


    /**
     * Network Byte Order
     * 
     * @param byts
     * @param offset
     * @return
     */
    public final static int byteArrayToIntInNBO(byte[] byts, int offset) {
        int intValue = 0;
        for (int i = 0; i < 4; i++) {
            int shift = (4 - 1 - i) * 8;
            intValue += (byts[i + offset] & 0x000000FF) << shift;
        }
        return intValue;
    }


    /**
     * Network Byte Order
     * 
     * @param intValue
     * @return
     */
    public final static byte[] intToByteArrayInNBO(int intValue) {
        byte[] byt = new byte[4];
        for (int i = 0; i < 4; i++) {
            byt[i] = (byte) (intValue >>> (24 - i * 8));
        }
        return byt;
    }


    /**
     * @param firstBytes
     * @param lastBytes
     * @return
     */
    public final static byte[] jogBytes(byte[]... byts) throws Exception {
        if (byts.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for (byte[] byt : byts) {
            if (byt == null) {
                continue;
            }
            out.write(byt);
        }
        try {
            out.flush();
        } catch (Exception e) {
            printException(e);
        }
        byte[] result = out.toByteArray();
        try {
            out.close();
        } catch (Exception e) {
            printException(e);
        }
        out = null;
        return result;
    }


    public final static String getStringFormAsset(Activity activity, String str) {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(activity.getAssets().open(str)), 8 * 1024);
            String line;
            StringBuilder buffer = new StringBuilder();
            while ((line = in.readLine()) != null)
                buffer.append(line).append('\n');
            return buffer.toString();
        } catch (IOException e) {
            Utils.printException(e);
            return "";
        } finally {
            if (in != null) {
                try {
                    in.close();
                    in = null;
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    printException(e);
                }
            }
        }
    }


    /**
     * 修正日期值的傳輸,將?”予以去除,月份日期小於10,則前面予以?
     * 
     * @param value
     * @return
     */
    public final static String adjustDate(String value) {
        // TODO Auto-generated method stub
        if (value.contains("-")) {
            String valueTemp = value;
            String year = valueTemp.substring(0, valueTemp.indexOf("-"));
            String month = valueTemp.substring(valueTemp.indexOf("-") + 1, valueTemp.lastIndexOf("-"));
            String day = valueTemp.substring(valueTemp.lastIndexOf("-") + 1);
            if (Integer.parseInt(month) < 10 && month.length() < 2)
                month = "0".concat(month);
            if (Integer.parseInt(day) < 10 && day.length() < 2)
                day = "0".concat(day);
            return year.concat(month).concat(day).replace("-", "");
        }
        return value;
    }


    /**
     * 解讀assets資料夾下的Config.txt檔案資訊
     * 
     * @param activity
     * @param tag
     *            需要在該檔案裡面查詢的key
     * @return 檔案中key對應的value
     */
    public final static String getConfigStringFormAsset(Activity activity, String tag) {
        if (configHm_ == null) {
            initConfigStringFormAsset(activity);
        }
        String value = configHm_.get(tag);
        return value == null ? "" : value;
    }


    /**
     * 解讀assets資料夾下的Config.txt檔案資訊
     * 
     * @param activity
     * @param tag
     *            需要在該檔案裡面查詢的key
     * @return 檔案中key對應的value
     */
    private final static void initConfigStringFormAsset(Activity activity) {
        //  只讀取一次Config檔案
        if (null == activity)
            return;
        configHm_ = new HashMap<String, String>();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(activity.getAssets().open("Config.txt")), 1024);
            String line;
            String key;
            String value;
            while ((line = in.readLine()) != null) {
                int index = line.indexOf(" ");
                if (index != -1) {
                    key = line.substring(0, index);
                    value = line.substring(index).trim();// 去掉前面的空?
                    configHm_.put(key, value);
                }
            }


            String str_lag = configHm_.get("jat_lag");
            jat_lag = Integer.parseInt(str_lag);
        } catch (Exception e) {
        } finally {
            if (in != null) {
                try {
                    in.close();
                    in = null;
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    printException(e);
                }
            }
        }
    }


    public final static int getScaledValueX(float num) {
        // TODO Auto-generated method stub
        float numtemp = SCALEDATEX * num;
        return (int) numtemp;
    }


    public final static int getFontSize(int size) {
        size = (int) (size * SCALEDFONT);
        return size;
    }


    public final static int getScaledValueY(float num) {
        // TODO Auto-generated method stub
        float numtemp = SCALEDATEY * num;
        return (int) numtemp;
    }


    /**
     * 
     * @param wRate
     *            參照寬度
     * @param hRate
     */
    private final static void setScaledParams(float wRate, float hRate) {
        // TODO Auto-generated method stub
        SCALEDATEX = wRate;
        SCALEDATEY = hRate;
    }


    // 計算介面寬高
    public final static void calculateScreenSize(Activity activity) {
        // 從Config配置文件中讀取服務端xml的基準解析度,該解析度應時刻與服務端定義的模板標準解析度一?
        BenchmarkresolutionW_ = Integer.parseInt(getConfigStringFormAsset(activity, "BenchmarkresolutionW"));
        BenchmarkresolutionH_ = Integer.parseInt(getConfigStringFormAsset(activity, "BenchmarkresolutionH"));


        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        Component.SCREENWIDTH = dm.widthPixels;
        // 該值為暫定值無實際意義,該值會在BaseView的onWindowFocusChanged方法中從新計算得?
        // Component.SCREENHEIGHT = dm.heightPixels;
        final float w = BenchmarkresolutionW_;
        final float h = BenchmarkresolutionH_;
        float wRate = dm.widthPixels / w;
        float hRate = dm.heightPixels / h;
        setScaledParams(wRate, hRate);
        setBrHeight();
        setMarginAndGap();
        SCALEDFONT = 1;
        int dw = dm.widthPixels;
        int dh = dm.heightPixels;
        float ds = dm.scaledDensity;


        if (dw <= 240 && dh <= 320) {
            if (ds != 0.75f) {
                SCALEDFONT = 0.75f / ds;
            }
        } else if (dw <= 320 && dh <= 500) {
            if (ds != 1f) {
                SCALEDFONT = 1f / ds;
            }
        } else if (dw <= 480 && dh <= 900) {
            if (ds != 1.5f) {
                SCALEDFONT = 1.5f / ds;
            }
        } else if (dw <= 600) {
            if (ds != 2f) {
                SCALEDFONT = 2f / ds;
            }
        }
    }


    private static void setMarginAndGap() {
        // TODO Auto-generated method stub
        String tempString = getConfigStringFormAsset(getActivity(), "verticalMarginOfB");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VMB = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的body框架縱向邊距經過縮放後為:" + LPLayout.VMB);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalMarginOfB");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HMB = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定body框架水平邊距經過縮放後為:" + LPLayout.HMB);


        tempString = getConfigStringFormAsset(getActivity(), "verticalGapOfB");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VGB = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的body框架子元素縱向間距經過縮放後為:" + LPLayout.VGB);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalGapOfB");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HGB = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的body框架子元素橫向間距經過縮放後為:" + LPLayout.HGB);


        tempString = getConfigStringFormAsset(getActivity(), "verticalMarginOfF");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VMF = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的form框架縱向邊距經過縮放後為:" + LPLayout.VMF);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalMarginOfF");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HMF = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的form框架水平邊距經過縮放後為:" + LPLayout.HMF);


        tempString = getConfigStringFormAsset(getActivity(), "verticalGapOfF");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VGF = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的form框架子元素縱向間距經過縮放後為:" + LPLayout.VGF);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalGapOfF");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HGF = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的form框架子元素橫向間距經過縮放後為:" + LPLayout.HGF);


        tempString = getConfigStringFormAsset(getActivity(), "verticalMarginOfD");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VMD = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的div框架縱向邊距經過縮放後為:" + LPLayout.VMD);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalMarginOfD");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HMD = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的div框架水平邊距經過縮放後為:" + LPLayout.HMD);


        tempString = getConfigStringFormAsset(getActivity(), "verticalGapOfD");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VGD = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的div框架子元素縱向間距經過縮放後為:" + LPLayout.VGD);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalGapOfD");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HGD = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的div框架子元素橫向間距經過縮放後為:" + LPLayout.HGD);


        tempString = getConfigStringFormAsset(getActivity(), "verticalMarginOfT");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VMT = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的td框架縱向邊距經過縮放後為:" + LPLayout.VMT);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalMarginOfT");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HMT = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的td框架水平邊距經過縮放後為:" + LPLayout.HMT);


        tempString = getConfigStringFormAsset(getActivity(), "verticalGapOfT");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.VGT = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的td框架子元素縱向間距經過縮放後為:" + LPLayout.VGT);


        tempString = getConfigStringFormAsset(getActivity(), "horizontalGapOfT");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        LPLayout.HGT = getScaledValueX(Integer.parseInt(tempString));
        printOutToConsole("Config設定的td框架子元素橫向間距經過縮放後為:" + LPLayout.HGT);
    }


    public static void setBrHeight() {
        // TODO Auto-generated method stub
        String tempString = getConfigStringFormAsset(getActivity(), "brHeight");
        if (null == tempString || tempString.equals(""))
            tempString = "0";
        BRHEIGHT = getScaledValueY(Integer.parseInt(tempString));
        printOutToConsole("Config設定的br節點高度經過縮放後為:" + BRHEIGHT);
    }


    public static int getBrHeight() {
        return BRHEIGHT;
    }


    /**
     * 獲取手機的model型號
     * 
     * @return
     */
    public final static String getPhoneModel() {
        String temp = android.os.Build.MODEL;
        temp = temp.replace(" ", "").replace("-", "_").trim();
        return temp;
    }


    public final static String getPhoneTarget() {
        String temp = android.os.Build.DEVICE;
        temp = temp.replace(" ", "").replace("-", "_").trim();
        return temp;


    }


    public final static String getClientID() {
        String temp = android.os.Build.ID;
        temp = temp.replace(" ", "").replace("-", "_").trim();
        return temp;
    }


    /**
     * 獲取介面解析度並且組合成?
     * 
     * @param activity
     * @return
     */
    public final static String getResolution(Activity activity) {
        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        String w = String.valueOf(dm.widthPixels);
        String h = String.valueOf(dm.heightPixels);
        return w.concat("*").concat(h);
    }


    public final static Typeface getTypeFace(String string) {
        Typeface tface = null;
        try {
            if (string.equalsIgnoreCase("bold")) {
                tface = Typeface.defaultFromStyle(Typeface.BOLD);
            } else if (string.equalsIgnoreCase("normal")) {
                tface = Typeface.defaultFromStyle(Typeface.NORMAL);
            } else {
                tface = null;
            }
        } catch (Exception e) {
            tface = null;
        }
        return tface;
    }


    /**
     * 設定字型是否粗體
     * */
    private final static boolean getFontType(String string) {
        if (string.equalsIgnoreCase("bold")) {
            return true;
        } else {
            return false;
        }
    }


    /** * 圖片去色,返回灰度圖片 * @param bmpOriginal 傳入的圖?* @return 去色後的圖片 */
    public final static Bitmap getGrayBitmap(Bitmap bmpOriginal) {
        if (null == bmpOriginal)
            return null;
        int width, height;
        height = bmpOriginal.getHeight();
        width = bmpOriginal.getWidth();
        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmpGrayscale);
        Paint paint = new Paint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(bmpOriginal, 0, 0, paint);
        return bmpGrayscale;
    }


    /**
     * @param activity
     * @param fileName
     * @param data
     */
    public final static void updateFile(Activity activity, String fileName, byte[] data) {
        try {
            FileOutputStream fos = activity.openFileOutput(fileName, Activity.MODE_PRIVATE);
            fos.write(new byte[] {}); // clear file.
            fos.write(data);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            Utils.printException(e);
        } catch (IOException e) {
            Utils.printException(e);
        } catch (Exception e) {
            Utils.printException(e);
        }


    }


    /**
     * @param activity
     * @param fullPathName
     * @return
     */
    public final static byte[] readFile(Activity activity, String fullPathName) {
        byte[] buffer = null;
        FileInputStream fis = null;
        try {
            File file = new File(fullPathName);
            if (file.exists()) {
                fis = activity.openFileInput(fullPathName);
                int len = fis.available();
                buffer = new byte[len];
                fis.read(buffer);
                fis.close();
            }
        } catch (FileNotFoundException e) {
            printException(e);
        } catch (IOException e) {
            printException(e);
        } catch (Exception e) {
            printException(e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return buffer;
    }


    public final static String getVersionName(Activity activity) {
        try {
            PackageManager pm = activity.getPackageManager();


            PackageInfo pi;


            pi = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
            return pi.versionName;
        } catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
            return "1.0";
        }


    }


    /**
     * User-Agent string we use in all of our HTTP transactions. XXX DO NOTE CHANGE THIS!!
     */
    public final static String makeUserAgent(String version) {
        StringBuffer buf = new StringBuffer("LightPole/");
        buf.append(version);
        buf.append("/");
        buf.append("android1.5");
        return buf.toString();
    }


    // Get a code from data.
    private final static int getCode(byte data[], int tree[]) {
        int node = tree[0];
        while (node >= 0) {
            if (bitIndex == 0)
                bitByte = (data[gIndex++] & 0xFF);
            node = (((bitByte & (1 << bitIndex)) == 0) ? tree[node >> 16] : tree[node & 0xFFFF]);
            bitIndex = (bitIndex + 1) & 0x7;
        }
        return (node & 0xFFFF);
    }


    /**
     * 判斷報文是否存在xml?如果沒有則手動加?
     * 
     * @param xml
     * @return
     */
    public final static String addXmlHead(String xml) {
        if (xml.indexOf("<?xml") == -1) {
            xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>".concat(xml);
        }
        return xml;
    }


    // get num bits from data. the num should not bigger than 32.
    private final static int getBits(byte[] data, int num) {
        int result = 0;
        int i = 0;
        if (bitIndex != 0) {
            i = 8 - bitIndex;
            result = bitByte >> bitIndex;
        }
        while (i < num) {
            bitByte = data[gIndex++] & 0xFF;
            result |= (bitByte << i);
            i += 8;
        }
        bitIndex = (bitIndex + num) & 0x7;// calculate (bitIndex + num)%8
        result &= (0xffffffff >>> (32 - num));
        return result;
    }


    // It's used to decompress gzip data. Re. rfc1951 and rfc 1952.
    public final static byte[] gunzip(byte[] gzipData) throws IOException {
        // Check if the data is gzip format and if it is used "deflate"
        // compression method.
        if (gzipData[0] != 31 || gzipData[1] != -117)
            throw new IOException("壓縮資料格式錯誤!");
        if (gzipData[2] != 8)
            throw new IOException("壓縮資料方法錯誤!");


        // Skip the gzip data header.
        byte flg = gzipData[3];
        gIndex = 10;
        bitIndex = 0;
        bitByte = 0;
        if ((flg & 4) != 0)
            gIndex += getBits(gzipData, 16);
        if ((flg & 8) != 0)
            while (gzipData[gIndex++] != 0)
                ;
        if ((flg & 16) != 0)
            while (gzipData[gIndex++] != 0)
                ;
        if ((flg & 2) != 0)
            gIndex += 2;


        int temp = gIndex;
        gIndex = gzipData.length - 4;
        // The size of the original input data
        int size = getBits(gzipData, 16) | (getBits(gzipData, 16) << 16);
        byte[] unzipData = new byte[size];
        gIndex = temp;


        int unzipIndex = 0;
        bitIndex = 0;
        bitByte = 0;
        int lastBlock = 0; // 0 indicates have more block when 1 indicates it is
        // the last block.
        int type = 0;// 0 indicates uncompress data, 1 indicates use final final static
        // Huffman, 2 indicates use dynamic Huffman.
        while (lastBlock == 0) {
            lastBlock = getBits(gzipData, 1);
            type = getBits(gzipData, 2);
            if (type == 0) {
                // uncompress data
                bitIndex = 0;
                int length = getBits(gzipData, 16);
                gIndex += 2;
                System.arraycopy(gzipData, gIndex, unzipData, unzipIndex, length);
                gIndex += length;
                unzipIndex += length;
            } else {
                byte[] lBits;
                byte[] dBits;
                if (type == 1) {
                    // use final final static huffman.
                    lBits = new byte[288];// The lTree node numbers 286, the
                    // last two nodes is use for full
                    // tree.
                    for (int i = 0; i < 288; i++) {
                        if (i < 144 || i > 279) {
                            lBits[i] = 8;
                        } else if (i < 256) {
                            lBits[i] = 9;
                        } else {
                            lBits[i] = 7;
                        }
                    }
                    dBits = new byte[32];// The dTree node numbers 30, the last
                    // two nodes is use for full tree.
                    for (int i = 0; i < dBits.length; i++) {
                        dBits[i] = 5;
                    }
                } else if (type == 2) {
                    // use dynamic huffman.
                    int hlit = getBits(gzipData, 5) + 257;
                    int hdist = getBits(gzipData, 5) + 1;
                    int hclen = getBits(gzipData, 4) + 4;
                    byte[] bltreeBits = new byte[19];// The blTree node numbers
                    // 19.
                    for (int i = 0; i < hclen; i++)
                        bltreeBits[DYNAMIC_L_ORDER[i]] = (byte) getBits(gzipData, 3);
                    int[] blTree = huffmanTree(bltreeBits);
                    lBits = decompressCode(gzipData, blTree, hlit);
                    dBits = decompressCode(gzipData, blTree, hdist);
                } else {
                    throw new IOException("不能正確解壓資料.");
                }


                int[] lTree = huffmanTree(lBits);// The literal/length tree.
                int[] dTree = huffmanTree(dBits);// The distance tree.
                int code = 0;
                while ((code = getCode(gzipData, lTree)) != 256) {// code=256
                    // indicates
                    // the end
                    // of this
                    // block.
                    if (code < 256) {// literal byte
                        unzipData[unzipIndex++] = (byte) code;
                    } else {// length/distance pairs
                        code -= 257;
                        int length = EXTRA_L_VALUES[code];
                        if (EXTRA_L_BITS[code] > 0)
                            length += getBits(gzipData, EXTRA_L_BITS[code]);
                        code = getCode(gzipData, dTree);
                        int distance = EXTRA_D_VALUES[code];
                        if (EXTRA_D_BITS[code] > 0)
                            distance += getBits(gzipData, EXTRA_D_BITS[code]);
                        while (distance < length) {
                            System.arraycopy(unzipData, unzipIndex - distance, unzipData, unzipIndex, distance);
                            unzipIndex += distance;
                            length -= distance;
                        }
                        System.arraycopy(unzipData, unzipIndex - distance, unzipData, unzipIndex, length);
                        unzipIndex += length;
                    }
                }
            }
        }
        return unzipData;
    }


    // Get a huffman tree.
    private final static int[] huffmanTree(byte bits[]) {
        int bl_count[] = new int[MAX_BITS + 1];
        for (int i = 0; i < bits.length; i++) {
            bl_count[bits[i]]++;
        }
        int code = 0;
        bl_count[0] = 0;
        int next_code[] = new int[MAX_BITS + 1];
        // Count the number of codes for each code length.
        for (int i = 1; i <= MAX_BITS; i++) {
            code = (code + bl_count[i - 1]) << 1;
            next_code[i] = code;
        }
        int tree[] = new int[((bits.length - 1) << 1) + MAX_BITS];
        int treeInsert = 1;
        for (int i = 0; i < bits.length; i++) {
            int len = bits[i];
            if (len != 0) {
                code = next_code[len]++;
                int node = 0;
                for (int bit = len - 1; bit >= 0; bit--) {
                    int value = code & (1 << bit);
                    if (value == 0) {
                        int left = tree[node] >> 16;
                        if (left == 0) {
                            tree[node] |= (treeInsert << 16);
                            node = treeInsert++;
                        } else
                            node = left;
                    } else {
                        int right = tree[node] & 0xFFFF;
                        if (right == 0) {
                            tree[node] |= treeInsert;
                            node = treeInsert++;
                        } else
                            node = right;
                    }
                }
                tree[node] = 0x80000000 | i;
            }
        }
        return tree;
    }


    // Decompress the literal/length code and the distance code.
    private final static byte[] decompressCode(byte data[], int blTree[], int count) {
        int code = 0;
        byte previousCode = 0;
        int times = 0; // The number of the previous code's length need to
        // repeat.
        byte treeBits[] = new byte[count];
        int index = 0;
        while (index < count) {
            code = getCode(data, blTree);
            if (code == 16) {
                times = getBits(data, 2) + 3;
            } else if (code == 17) {
                times = getBits(data, 3) + 3;
                previousCode = 0;
            } else if (code == 18) {
                times = getBits(data, 7) + 11;
                previousCode = 0;
            } else {
                times = 0;
                previousCode = (byte) code;
                treeBits[index++] = (byte) code;
            }
            for (int i = 0; i < times; i++) {
                treeBits[index++] = previousCode;
            }
        }
        return treeBits;
    }


    public final static String getVersion() {
        // 獲取手機作業系統版本?
        return android.os.Build.VERSION.RELEASE;
    }


    public final static String getModel() {
        // 獲取手機型號
        return android.os.Build.MODEL;
    }


    public final static String getName() {
        // 獲取使用者姓???
        return android.os.Build.USER;
    }


    public final static String getDeviceName() {
        return android.os.Build.DEVICE;
    }


    public final static String getPlayform() {
        // 當前系統平臺
        return "Android";
    }


    public final static String getUUID() {
        final TelephonyManager tm = (TelephonyManager) activity_.getBaseContext().getSystemService(
                Context.TELEPHONY_SERVICE);
        final String tmDevice, tmSerial, tmPhone, androidId;
        tmDevice = "" + tm.getDeviceId();
        tmSerial = "" + tm.getSimSerialNumber();
        androidId = ""
                + android.provider.Settings.Secure.getString(activity_.getContentResolver(),
                        android.provider.Settings.Secure.ANDROID_ID);
        UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
        return deviceUuid.toString();
    }


    public final static String connectType(Activity bv) {
        // TODO Auto-generated method stub
        try {
            Context context = bv.getApplicationContext();
            ConnectivityManager connectivity = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);


            NetworkInfo ni = connectivity.getActiveNetworkInfo();


            if ((null != ni) && ni.isAvailable()) {
                return ni.getTypeName();
            }
        } catch (Exception e) {
            Utils.printException(e);
            return getConfigStringFormAsset(getActivity(), "httpError");
        }
        return getConfigStringFormAsset(getActivity(), "httpError");


    }


    private final static String getLocationByNet(Activity bv) {
        // TODO Auto-generated method stub
        double LATITUDE = 0;
        double LONGITUDE = 0;
        int timeoutConnection = 3000;
        int timeoutSocket = 5000;
        TelephonyManager mTelNet = (TelephonyManager) bv.getSystemService(Context.TELEPHONY_SERVICE);
        String operator = mTelNet.getNetworkOperator();
        CellLocation cl = mTelNet.getCellLocation();
        // #ifdef Android2.2
        if (cl instanceof CdmaCellLocation) {
            CdmaCellLocation location = (CdmaCellLocation) cl;
            LATITUDE = (double) location.getBaseStationLatitude() / 14400;// 14400?*90/1296000
            LONGITUDE = (double) location.getBaseStationLongitude() / 14400;


        } else if (null != operator && !operator.equals("")) {
            // #else
            // @ if (null != operator && !operator.equals("")) {
            // #endif
            String mcc = operator.substring(0, 3);
            String mnc = operator.substring(3);


            if (cl instanceof GsmCellLocation) {
                GsmCellLocation location = (GsmCellLocation) cl;
                if (null != location) {
                    int cid = location.getCid();
                    int lac = location.getLac();
                    HttpParams httpParameters = new BasicHttpParams();
                    HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
                    HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
                    HttpClient httpclient = new DefaultHttpClient(httpParameters);
                    HttpPost post = new HttpPost("http://www.google.com/loc/json");
                    try {
                        JSONObject holder = new JSONObject();
                        holder.put("version", "1.1.0");
                        holder.put("host", "maps.google.com");
                        holder.put("address_language", "zh_CN");
                        holder.put("request_address", true);
                        JSONObject tower = new JSONObject();
                        tower.put("mobile_country_code", mcc);
                        tower.put("mobile_network_code", mnc);
                        tower.put("cell_id", cid);
                        tower.put("location_area_code", lac);
                        JSONArray towerarray = new JSONArray();
                        towerarray.put(tower);
                        holder.put("cell_towers", towerarray);
                        StringEntity query = new StringEntity(holder.toString());
                        post.setEntity(query);
                        HttpResponse response = httpclient.execute(post);
                        HttpEntity entity = response.getEntity();
                        BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                        StringBuffer strBuff = new StringBuffer();
                        String result = null;
                        while ((result = buffReader.readLine()) != null) {
                            strBuff.append(result);
                        }
                        JSONObject json = new JSONObject(strBuff.toString());
                        JSONObject subjosn = new JSONObject(json.getString("location"));
                        LATITUDE = Double.parseDouble(subjosn.getString("latitude"));
                        LONGITUDE = Double.parseDouble(subjosn.getString("longitude"));
                    } catch (Exception e) {
                        // LPUtils.printException(e);
                        Utils.printException(e);
                    }
                }
            }
        }


        return (String.valueOf(LATITUDE)).concat(",").concat(String.valueOf(LONGITUDE));
    }


    /**
     * 檢視android手機是否被刷過機.linux系統下root許可權可以使用su命令,android本身也是小型的linux系統
     * 
     * @param command
     *            "id" 或其他的 su命令
     */
    public final static boolean runRootCommand(String command) {


        Process process = null;
        DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
        } catch (Exception e) {
            printException(e);
            return false;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                process.destroy();
            } catch (Exception e) {
                // nothing
            }
        }
        return true;
    }


    /**
     * * 儲存物件到文?* * @param obj * @param fileName * @throws Exception
     * 
     * @param publicKey
     */
    public static void saveFile(Activity activity, Object obj, String fileName) {
        ObjectOutputStream output;
        try {
            output = new ObjectOutputStream(activity.openFileOutput(fileName, Context.MODE_PRIVATE));


            output.writeObject(obj);
            output.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            printException(e);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            printException(e);
        }
    }


    /**
     * 讀取Config的文字預設顏色設定,如果沒有,預設為黑色
     * 
     * @param activity
     */
    public static void readTextClolr(Activity activity) {
        String colortemp = getConfigStringFormAsset(activity, "textColor");
        Component.TEXTCOLOR = Color.BLACK;
        if (null != colortemp && !colortemp.equals("")) {
            if (colortemp.equalsIgnoreCase("black"))
                Component.TEXTCOLOR = Color.BLACK;
            else if (colortemp.equalsIgnoreCase("BLUE"))
                Component.TEXTCOLOR = Color.BLUE;
            else if (colortemp.equalsIgnoreCase("CYAN"))
                Component.TEXTCOLOR = Color.CYAN;
            else if (colortemp.equalsIgnoreCase("dkgray"))
                Component.TEXTCOLOR = Color.DKGRAY;
            else if (colortemp.equalsIgnoreCase("white"))
                Component.TEXTCOLOR = Color.WHITE;
            else if (colortemp.equalsIgnoreCase("gray"))
                Component.TEXTCOLOR = Color.GRAY;
            else if (colortemp.equalsIgnoreCase("green"))
                Component.TEXTCOLOR = Color.GREEN;
            else if (colortemp.equalsIgnoreCase("ltgray"))
                Component.TEXTCOLOR = Color.LTGRAY;
            else if (colortemp.equalsIgnoreCase("magenta"))
                Component.TEXTCOLOR = Color.MAGENTA;
            else if (colortemp.equalsIgnoreCase("red"))
                Component.TEXTCOLOR = Color.RED;
            else if (colortemp.equalsIgnoreCase("transparent"))
                Component.TEXTCOLOR = Color.TRANSPARENT;
            else if (colortemp.equalsIgnoreCase("yellow"))
                Component.TEXTCOLOR = Color.YELLOW;
        }
    }


    /** * 從檔案讀取object * * @param fileName * @return * @throws Exception */
    public static Object readFromFile(Activity activity, String fileName) {
        ObjectInputStream input;
        try {
            input = new ObjectInputStream(activity.openFileInput(fileName));
            Object obj = input.readObject();
            input.close();
            return obj;
        } catch (StreamCorruptedException e) {
            // TODO Auto-generated catch block
            printException(e);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            printException(e);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            printException(e);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            printException(e);
        }
        return null;
    }


    public static void saveFile(String name, byte[] buffer) {
        if (null == name || name.equals(""))
            return;
        name = OffStoreDownload.FILEROOT.concat(name);


        if (name.endsWith("/")) {
            createPath(name);
        } else {
            createFile(name, buffer);
        }
    }


    /**
     * @param activity
     * @param fullPathName
     * @return
     */
    public final static byte[] readFile(String fullPathName) {
        byte[] buffer = null;
        try {
            File file = new File(fullPathName);
            FileInputStream fis = new FileInputStream(file);
            int len = fis.available();
            buffer = new byte[len];
            fis.read(buffer);
            fis.close();
            buffer = AESCipher.decrypt(buffer, AESCipher.customerKey_, AESCipher.customerIv_);
        } catch (FileNotFoundException e) {
            printException(e);
        } catch (IOException e) {
            printException(e);
        } catch (Exception e) {
            printException(e);
        }
        return buffer;
    }


    private static void createFile(String path, byte[] buffer) {
        // TODO Auto-generated method stub
        File file = null;
        FileOutputStream fstream = null;
        try {
            file = new File(path);
            if (!file.exists()) {
                file.createNewFile();
                fstream = new FileOutputStream(file);
                buffer = AESCipher.encrypt(buffer, AESCipher.customerKey_, AESCipher.customerIv_);
                fstream.write(buffer);
            }
        } catch (Exception e) {


            printException(e);


        } finally {


            if (fstream != null) {


                try {
                    fstream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    printException(e);
                }


            }


        }
    }


    public static void createPath(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }


    /**
     * 初始化本地儲存的 key,iv
     * 
     * @param activity
     */
    public static void initCustomerSecretKey(Activity activity) {
        try {
            TelephonyManager tm = (TelephonyManager) activity.getSystemService(Activity.TELEPHONY_SERVICE);
            String deviceId = tm.getDeviceId();
            String deviceName = Build.DEVICE;
            String packageName = activity.getPackageName();
            if (deviceId == null) {
                deviceId = "";
            }
            if (deviceName == null) {
                deviceName = "";
            }
            if (packageName == null) {
                packageName = "com.rytong.tools";
            }
            String message = deviceId.concat(deviceName).concat(packageName)
                    .concat(RSACipher.PUBLIC_CER_MODULUS_FILENAME).concat(HMac.KEY_MAC_MD5);
            byte[] sha1 = HMac.SHA1(message.getBytes());
            byte[] md5 = MD5.getMD5(message.getBytes());
            byte[] secretKey = new byte[sha1.length + md5.length];
            System.arraycopy(sha1, 0, secretKey, 0, sha1.length);
            System.arraycopy(md5, 0, secretKey, sha1.length, md5.length);
            AESCipher.customerKey_ = ClientHello.getAESKey(secretKey);
            AESCipher.customerIv_ = new byte[16];
            for (int i = 0; i < 16; i++) {
                AESCipher.customerIv_[i] = secretKey[secretKey.length - 1 - i];
            }
        } catch (Exception e) {
            printOutToConsole(e);
        }
    }


    /**
     * 清空介面所有控制元件
     */
    public static void clearWidget() {
        // TODO Auto-generated method stub
        if (null != Component.VWIDGETARRAY && !Component.VWIDGETARRAY.isEmpty()) {
            int size = Component.VWIDGETARRAY.size();
            Component comp;
            int index = Integer.MIN_VALUE;
            for (int i = 0; i < size; i++) {
                comp = (Component) Component.VWIDGETARRAY.get(i);
                if (null != comp.getTag() && comp.getTag().equalsIgnoreCase("body"))
                    index = i;
            }
            if (index != Integer.MIN_VALUE) {
                comp = (Component) Component.VWIDGETARRAY.get(index);
                comp.releaseResource(comp);
            }
        }
    }


}

相關文章