Add 'android/commons/' from commit '2f13cb5b5bc54ff5a3217c9d19b150b45363dc8f'

git-subtree-dir: android/commons
git-subtree-mainline: d1937cbcfb25c713a7c39feeed41aa86724273d8
git-subtree-split: 2f13cb5b5bc54ff5a3217c9d19b150b45363dc8f
diff --git a/android/commons/build.gradle b/android/commons/build.gradle
new file mode 100644
index 0000000..d6d07eb
--- /dev/null
+++ b/android/commons/build.gradle
@@ -0,0 +1,77 @@
+plugins {
+    id "com.github.hierynomus.license" version "0.14.0"
+}
+
+apply plugin: 'com.android.library'
+
+android {
+
+    compileSdkVersion project.compileSdkVersion
+//    buildToolsVersion project.buildToolsVersion
+    resourcePrefix "weexcomm"
+
+    defaultConfig {
+        minSdkVersion project.minSdkVersion
+        targetSdkVersion project.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    lintOptions {
+          abortOnError false
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+
+        debug {
+            jniDebuggable true
+            debuggable true
+
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation project(':weex_sdk')
+    implementation "com.android.support:support-v4:${project.supportLibVersion}"
+    implementation "com.android.support:appcompat-v7:${project.supportLibVersion}"
+    // implementation project(':weex_sdk')
+    implementation 'com.squareup.picasso:picasso:2.5.2'
+    implementation 'com.facebook.fresco:fresco:0.10.0'
+    implementation 'com.taobao.android.weex_inspection:protocol:1.1.4.1'
+
+    compileOnly 'com.taobao.android:weex_analyzer:0.1.0.5'
+    compileOnly 'com.squareup.okhttp:okhttp:2.3.0'
+    compileOnly 'com.squareup.okhttp:okhttp-ws:2.3.0'
+    compileOnly "com.alibaba:fastjson:${project.fastjsonLibVersion}"
+
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.hamcrest:hamcrest-core:1.3'
+    testImplementation 'org.javassist:javassist:3.20.0-GA'
+    testImplementation 'org.mockito:mockito-core:1.10.19'
+    testImplementation 'org.objenesis:objenesis:2.1'
+    testImplementation 'org.powermock:powermock-core:1.6.4'
+    testImplementation 'org.powermock:powermock-api-mockito:1.6.4'
+    testImplementation 'org.powermock:powermock-module-junit4-common:1.6.4'
+    testImplementation 'org.powermock:powermock-module-junit4:1.6.4'
+    testImplementation 'org.powermock:powermock-module-junit4-legacy:1.6.4'
+    testImplementation 'org.powermock:powermock-module-testng:1.6.4'
+    testImplementation 'org.robolectric:robolectric:3.0-rc3'
+}
+
+if(file('../license/LICENSE').exists()){
+    license {
+        header = file('../license/LICENSE')
+    }
+    preBuild.dependsOn licenseFormat
+}
\ No newline at end of file
diff --git a/android/commons/proguard-rules.pro b/android/commons/proguard-rules.pro
new file mode 100644
index 0000000..4acd43d
--- /dev/null
+++ b/android/commons/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/sospartan/sdks/android_sdk20130219/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/android/commons/src/androidTest/java/com/alibaba/weex/commons/ApplicationTest.java b/android/commons/src/androidTest/java/com/alibaba/weex/commons/ApplicationTest.java
new file mode 100644
index 0000000..d8cab6d
--- /dev/null
+++ b/android/commons/src/androidTest/java/com/alibaba/weex/commons/ApplicationTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+  public ApplicationTest() {
+    super(Application.class);
+  }
+}
diff --git a/android/commons/src/main/AndroidManifest.xml b/android/commons/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..af18743
--- /dev/null
+++ b/android/commons/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!-- 
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.alibaba.weex.commons">
+
+
+
+</manifest>
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/AbstractWeexActivity.java b/android/commons/src/main/java/com/alibaba/weex/commons/AbstractWeexActivity.java
new file mode 100644
index 0000000..8854ce2
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/AbstractWeexActivity.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.weex.commons.util.AssertUtil;
+import com.alibaba.weex.commons.util.ScreenUtil;
+import com.taobao.weex.IWXRenderListener;
+import com.taobao.weex.WXEnvironment;
+import com.taobao.weex.WXSDKInstance;
+import com.taobao.weex.common.Constants;
+import com.taobao.weex.common.WXRenderStrategy;
+import com.taobao.weex.utils.WXUtils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by sospartan on 5/30/16.
+ */
+public abstract class AbstractWeexActivity extends AppCompatActivity implements IWXRenderListener {
+  private static final String TAG = "AbstractWeexActivity";
+
+  private ViewGroup mContainer;
+  private WXSDKInstance mInstance;
+
+  protected WXAnalyzerDelegate mWxAnalyzerDelegate;
+
+  @Override
+  protected void onCreate(@Nullable Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    createWeexInstance();
+    mInstance.onActivityCreate();
+
+    mWxAnalyzerDelegate = new WXAnalyzerDelegate(this);
+    mWxAnalyzerDelegate.onCreate();
+    getWindow().setFormat(PixelFormat.TRANSLUCENT);
+  }
+
+  protected final void setContainer(ViewGroup container){
+    mContainer = container;
+  }
+
+  protected final ViewGroup getContainer(){
+    return mContainer;
+  }
+
+  protected void destoryWeexInstance(){
+    if(mInstance != null){
+      mInstance.registerRenderListener(null);
+      mInstance.destroy();
+      mInstance = null;
+    }
+  }
+
+  protected void createWeexInstance(){
+    destoryWeexInstance();
+
+    Rect outRect = new Rect();
+    getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
+
+    mInstance = new WXSDKInstance(this);
+    mInstance.registerRenderListener(this);
+  }
+
+  protected void renderPage(String template,String source){
+    renderPage(template,source,null);
+  }
+
+  protected void renderPage(String template,String source,String jsonInitData){
+    AssertUtil.throwIfNull(mContainer,new RuntimeException("Can't render page, container is null"));
+    Map<String, Object> options = new HashMap<>();
+    options.put(WXSDKInstance.BUNDLE_URL, source);
+    // Set options.bundleDigest
+    try {
+      String banner = WXUtils.getBundleBanner(template);
+      JSONObject jsonObj = JSONObject.parseObject(banner);
+      String digest = null;
+      if (jsonObj != null) {
+        digest = jsonObj.getString(Constants.CodeCache.BANNER_DIGEST);
+      }
+      if (digest != null) {
+        options.put(Constants.CodeCache.DIGEST, digest);
+      }
+    } catch (Throwable t) {}
+    //Set options.codeCachePath
+    String path = WXEnvironment.getFilesDir(getApplicationContext());
+    path += File.separator;
+    path += Constants.CodeCache.SAVE_PATH;
+    path += File.separator;
+    options.put(Constants.CodeCache.PATH, path);
+
+    mInstance.setTrackComponent(true);
+    mInstance.render(
+      getPageName(),
+      template,
+      options,
+      jsonInitData,
+      WXRenderStrategy.APPEND_ASYNC);
+  }
+
+  protected void renderPageByURL(String url){
+    renderPageByURL(url,null);
+  }
+
+  protected void renderPageByURL(String url,String jsonInitData){
+    AssertUtil.throwIfNull(mContainer,new RuntimeException("Can't render page, container is null"));
+    Map<String, Object> options = new HashMap<>();
+    options.put(WXSDKInstance.BUNDLE_URL, url);
+    mInstance.setTrackComponent(true);
+    mInstance.renderByUrl(
+      getPageName(),
+      url,
+      options,
+      jsonInitData,
+      WXRenderStrategy.APPEND_ASYNC);
+  }
+
+  protected String getPageName(){
+    return TAG;
+  }
+
+  @Override
+  public void onStart() {
+    super.onStart();
+    if(mInstance!=null){
+      mInstance.onActivityStart();
+    }
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onStart();
+    }
+  }
+
+  @Override
+  public void onResume() {
+    super.onResume();
+    if(mInstance!=null){
+      mInstance.onActivityResume();
+    }
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onResume();
+    }
+  }
+
+  @Override
+  public void onPause() {
+    super.onPause();
+    if(mInstance!=null){
+      mInstance.onActivityPause();
+    }
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onPause();
+    }
+  }
+
+  @Override
+  public void onStop() {
+    super.onStop();
+    if(mInstance!=null){
+      mInstance.onActivityStop();
+    }
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onStop();
+    }
+  }
+
+  @Override
+  public void onDestroy() {
+    super.onDestroy();
+    if(mInstance!=null){
+      mInstance.onActivityDestroy();
+    }
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onDestroy();
+    }
+  }
+
+  @Override
+  public void onViewCreated(WXSDKInstance wxsdkInstance, View view) {
+    View wrappedView = null;
+    if(mWxAnalyzerDelegate != null){
+      wrappedView = mWxAnalyzerDelegate.onWeexViewCreated(wxsdkInstance,view);
+    }
+    if(wrappedView != null){
+      view = wrappedView;
+    }
+    if (mContainer != null) {
+      mContainer.removeAllViews();
+      mContainer.addView(view);
+    }
+  }
+
+
+
+  @Override
+  public void onRefreshSuccess(WXSDKInstance wxsdkInstance, int i, int i1) {
+
+  }
+
+  @Override
+  @CallSuper
+  public void onRenderSuccess(WXSDKInstance instance, int width, int height) {
+    if(mWxAnalyzerDelegate  != null){
+      mWxAnalyzerDelegate.onWeexRenderSuccess(instance);
+    }
+  }
+
+  @Override
+  @CallSuper
+  public void onException(WXSDKInstance instance, String errCode, String msg) {
+    if(mWxAnalyzerDelegate != null){
+      mWxAnalyzerDelegate.onException(instance,errCode,msg);
+    }
+  }
+
+  @Override
+  @CallSuper
+  public boolean onKeyUp(int keyCode, KeyEvent event) {
+    return (mWxAnalyzerDelegate != null && mWxAnalyzerDelegate.onKeyUp(keyCode,event)) || super.onKeyUp(keyCode, event);
+  }
+
+  @Override
+  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    if (mInstance != null) {
+      mInstance.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/SimpleWeexActivity.java b/android/commons/src/main/java/com/alibaba/weex/commons/SimpleWeexActivity.java
new file mode 100644
index 0000000..91e0d1a
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/SimpleWeexActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.ViewGroup;
+import com.taobao.weex.WXSDKInstance;
+
+/**
+ * Basic Weex powered Activity.
+ * Created by sospartan on 5/31/16.
+ */
+public abstract class SimpleWeexActivity extends AbstractWeexActivity {
+
+  @Override
+  protected void onCreate(@Nullable Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContainer((ViewGroup) findViewById(android.R.id.content));
+  }
+
+  @Override
+  public void onRenderSuccess(WXSDKInstance instance, int width, int height) {
+    super.onRenderSuccess(instance,width,height);
+  }
+
+  @Override
+  public void onException(WXSDKInstance instance, String errCode, String msg) {
+    super.onException(instance,errCode,msg);
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/WXAnalyzerDelegate.java b/android/commons/src/main/java/com/alibaba/weex/commons/WXAnalyzerDelegate.java
new file mode 100644
index 0000000..f9ad58e
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/WXAnalyzerDelegate.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.taobao.weex.WXSDKInstance;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Description:
+ * <p>
+ * Created by rowandjj(chuyi)<br/>
+ * Date: 2016/10/27<br/>
+ * Time: 下午7:01<br/>
+ */
+
+public final class WXAnalyzerDelegate {
+    private Object mWXAnalyzer;
+
+    private static boolean ENABLE = false;
+
+    @SuppressWarnings("unchecked")
+    public WXAnalyzerDelegate(@Nullable Context context) {
+        if(!ENABLE){
+            return;
+        }
+        if(context == null){
+            return;
+        }
+        try {
+            Class clazz = Class.forName("com.taobao.weex.analyzer.WeexDevOptions");
+            Constructor constructor = clazz.getDeclaredConstructor(Context.class);
+            mWXAnalyzer = constructor.newInstance(context);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void onCreate() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onCreate");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void onStart() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onStart");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void onResume() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onResume");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void onPause() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onPause");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void onStop() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onStop");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void onDestroy() {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onDestroy");
+            method.invoke(mWXAnalyzer);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void onWeexRenderSuccess(@Nullable WXSDKInstance instance) {
+        if (mWXAnalyzer == null || instance == null) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onWeexRenderSuccess", WXSDKInstance.class);
+            method.invoke(mWXAnalyzer, instance);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mWXAnalyzer == null) {
+            return false;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onKeyUp", int.class, KeyEvent.class);
+            return (boolean) method.invoke(mWXAnalyzer, keyCode, event);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public void onException(WXSDKInstance instance, String errCode, String msg) {
+        if (mWXAnalyzer == null) {
+            return;
+        }
+        if (TextUtils.isEmpty(errCode) && TextUtils.isEmpty(msg)) {
+            return;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onException", WXSDKInstance.class, String.class, String.class);
+            method.invoke(mWXAnalyzer, instance, errCode, msg);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public View onWeexViewCreated(WXSDKInstance instance, View view) {
+        if (mWXAnalyzer == null || instance == null || view == null) {
+            return null;
+        }
+        try {
+            Method method = mWXAnalyzer.getClass().getDeclaredMethod("onWeexViewCreated", WXSDKInstance.class, View.class);
+            View retView = (View) method.invoke(mWXAnalyzer, instance, view);
+            return retView;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return view;
+        }
+    }
+
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTool.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTool.java
new file mode 100644
index 0000000..53eb456
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTool.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.graphics.Bitmap;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.taobao.weex.utils.WXLogUtils;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Created by rowandjj(chuyi)<br/>
+ */
+
+public class BlurTool {
+
+    public interface OnBlurCompleteListener {
+        /**
+         * blur complete event.(Notice:in sub thread)
+         *
+         * @param bitmap the blurred bitmap
+         * */
+        void onBlurComplete(@NonNull Bitmap bitmap);
+    }
+
+    private static ExecutorService sExecutorService = Executors.newCachedThreadPool(new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable r) {
+            return new Thread(r,"wx_blur_thread");
+        }
+    });
+
+    private static final String TAG = "BlurTool";
+
+    /**
+     * radius in [0,10]
+     * */
+    @NonNull
+    @SuppressWarnings("unused")
+    public static Bitmap blur(@NonNull Bitmap originalImage, int radius) {
+        long start = System.currentTimeMillis();
+        radius = Math.min(10, Math.max(0,radius));//[0,10]
+        if(radius == 0) {
+            return originalImage;
+        }
+        int width = originalImage.getWidth();
+        int height = originalImage.getHeight();
+
+        if(width <= 0 || height <= 0) {
+            return originalImage;
+        }
+
+        double sampling = calculateSampling(radius);
+        int retryTimes = 3;
+        Bitmap sampledImage = Bitmap.createScaledBitmap(originalImage,(int)(sampling*width),(int)(sampling*height),true);
+        for(int i = 0; i < retryTimes; i++) {
+            try {
+                if(radius == 0) {
+                    return originalImage;
+                }
+                double s = calculateSampling(radius);
+                if(s != sampling) {
+                    sampling = s;
+                    sampledImage = Bitmap.createScaledBitmap(originalImage,(int)(sampling*width),(int)(sampling*height),true);
+                }
+
+                Bitmap result = stackBlur(sampledImage,radius);
+                return result;
+            }catch (Exception e) {
+                WXLogUtils.e(TAG, "thrown exception when blurred image(times = " + i + "),"+ e.getMessage());
+                radius -= 1;
+                radius = Math.max(0,radius);
+            }
+        }
+        WXLogUtils.d(TAG, "elapsed time on blurring image(radius:"+ radius + ",sampling: " + sampling + "): " + (System.currentTimeMillis() - start) + "ms");
+        return originalImage;
+    }
+
+    private static double calculateSampling(int radius){
+        double sampling;
+        if(radius <= 3) {
+            sampling = 1/(double)2;
+        }else if(radius <= 8) {
+            sampling = 1/(double)4;
+        }else {
+            sampling = 1/(double)8;
+        }
+
+        return sampling;
+    }
+
+    @SuppressWarnings("unused")
+    public static void asyncBlur(@NonNull final Bitmap originalImage, final int radius, @Nullable final OnBlurCompleteListener listener) {
+        sExecutorService.execute(new Runnable() {
+            @Override
+            public void run() {
+                if(listener != null) {
+                    listener.onBlurComplete(blur(originalImage,radius));
+                }
+            }
+        });
+    }
+
+    private static Bitmap stackBlur(Bitmap sentBitmap, int radius) {
+        // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
+        Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
+
+        if (radius < 1) {
+            return (null);
+        }
+
+        int w = bitmap.getWidth();
+        int h = bitmap.getHeight();
+
+        int[] pix = new int[w * h];
+        bitmap.getPixels(pix, 0, w, 0, 0, w, h);
+
+        int wm = w - 1;
+        int hm = h - 1;
+        int wh = w * h;
+        int div = radius + radius + 1;
+
+        int r[] = new int[wh];
+        int g[] = new int[wh];
+        int b[] = new int[wh];
+        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+        int vmin[] = new int[Math.max(w, h)];
+
+        int divsum = (div + 1) >> 1;
+        divsum *= divsum;
+        int dv[] = new int[256 * divsum];
+        for (i = 0; i < 256 * divsum; i++) {
+            dv[i] = (i / divsum);
+        }
+
+        yw = yi = 0;
+
+        int[][] stack = new int[div][3];
+        int stackpointer;
+        int stackstart;
+        int[] sir;
+        int rbs;
+        int r1 = radius + 1;
+        int routsum, goutsum, boutsum;
+        int rinsum, ginsum, binsum;
+
+        for (y = 0; y < h; y++) {
+            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+            for (i = -radius; i <= radius; i++) {
+                p = pix[yi + Math.min(wm, Math.max(i, 0))];
+                sir = stack[i + radius];
+                sir[0] = (p & 0xff0000) >> 16;
+                sir[1] = (p & 0x00ff00) >> 8;
+                sir[2] = (p & 0x0000ff);
+                rbs = r1 - Math.abs(i);
+                rsum += sir[0] * rbs;
+                gsum += sir[1] * rbs;
+                bsum += sir[2] * rbs;
+                if (i > 0) {
+                    rinsum += sir[0];
+                    ginsum += sir[1];
+                    binsum += sir[2];
+                } else {
+                    routsum += sir[0];
+                    goutsum += sir[1];
+                    boutsum += sir[2];
+                }
+            }
+            stackpointer = radius;
+
+            for (x = 0; x < w; x++) {
+
+                r[yi] = dv[rsum];
+                g[yi] = dv[gsum];
+                b[yi] = dv[bsum];
+
+                rsum -= routsum;
+                gsum -= goutsum;
+                bsum -= boutsum;
+
+                stackstart = stackpointer - radius + div;
+                sir = stack[stackstart % div];
+
+                routsum -= sir[0];
+                goutsum -= sir[1];
+                boutsum -= sir[2];
+
+                if (y == 0) {
+                    vmin[x] = Math.min(x + radius + 1, wm);
+                }
+                p = pix[yw + vmin[x]];
+
+                sir[0] = (p & 0xff0000) >> 16;
+                sir[1] = (p & 0x00ff00) >> 8;
+                sir[2] = (p & 0x0000ff);
+
+                rinsum += sir[0];
+                ginsum += sir[1];
+                binsum += sir[2];
+
+                rsum += rinsum;
+                gsum += ginsum;
+                bsum += binsum;
+
+                stackpointer = (stackpointer + 1) % div;
+                sir = stack[(stackpointer) % div];
+
+                routsum += sir[0];
+                goutsum += sir[1];
+                boutsum += sir[2];
+
+                rinsum -= sir[0];
+                ginsum -= sir[1];
+                binsum -= sir[2];
+
+                yi++;
+            }
+            yw += w;
+        }
+        for (x = 0; x < w; x++) {
+            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+            yp = -radius * w;
+            for (i = -radius; i <= radius; i++) {
+                yi = Math.max(0, yp) + x;
+
+                sir = stack[i + radius];
+
+                sir[0] = r[yi];
+                sir[1] = g[yi];
+                sir[2] = b[yi];
+
+                rbs = r1 - Math.abs(i);
+
+                rsum += r[yi] * rbs;
+                gsum += g[yi] * rbs;
+                bsum += b[yi] * rbs;
+
+                if (i > 0) {
+                    rinsum += sir[0];
+                    ginsum += sir[1];
+                    binsum += sir[2];
+                } else {
+                    routsum += sir[0];
+                    goutsum += sir[1];
+                    boutsum += sir[2];
+                }
+
+                if (i < hm) {
+                    yp += w;
+                }
+            }
+            yi = x;
+            stackpointer = radius;
+            for (y = 0; y < h; y++) {
+                // Preserve alpha channel: ( 0xff000000 & pix[yi] )
+                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+
+                rsum -= routsum;
+                gsum -= goutsum;
+                bsum -= boutsum;
+
+                stackstart = stackpointer - radius + div;
+                sir = stack[stackstart % div];
+
+                routsum -= sir[0];
+                goutsum -= sir[1];
+                boutsum -= sir[2];
+
+                if (x == 0) {
+                    vmin[y] = Math.min(y + r1, hm) * w;
+                }
+                p = x + vmin[y];
+
+                sir[0] = r[p];
+                sir[1] = g[p];
+                sir[2] = b[p];
+
+                rinsum += sir[0];
+                ginsum += sir[1];
+                binsum += sir[2];
+
+                rsum += rinsum;
+                gsum += ginsum;
+                bsum += binsum;
+
+                stackpointer = (stackpointer + 1) % div;
+                sir = stack[stackpointer];
+
+                routsum += sir[0];
+                goutsum += sir[1];
+                boutsum += sir[2];
+
+                rinsum -= sir[0];
+                ginsum -= sir[1];
+                binsum -= sir[2];
+
+                yi += w;
+            }
+        }
+
+        bitmap.setPixels(pix, 0, w, 0, 0, w, h);
+
+        return (bitmap);
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTransformation.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTransformation.java
new file mode 100644
index 0000000..b530b92
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/BlurTransformation.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.graphics.Bitmap;
+
+import com.squareup.picasso.Transformation;
+
+public class BlurTransformation implements Transformation {
+
+  private int mRadius;
+
+  public BlurTransformation(int radius) {
+    mRadius = radius;
+  }
+
+  @Override public Bitmap transform(Bitmap source) {
+    if(mRadius <= 0) {
+      return source;
+    }
+    Bitmap bitmap;
+    try {
+      bitmap = BlurTool.blur(source, mRadius);
+    }catch (Exception e){
+      bitmap = source;
+    }
+    if(bitmap != source) {
+      source.recycle();
+    }
+    return bitmap;
+  }
+
+  @Override public String key() {
+    return "BlurTransformation(radius=" + mRadius + ")";
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapter.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapter.java
new file mode 100644
index 0000000..22810dc
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.support.annotation.Nullable;
+
+import com.alibaba.weex.commons.util.WSEventReporter;
+import com.squareup.okhttp.Headers;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+import com.squareup.okhttp.ws.WebSocket;
+import com.squareup.okhttp.ws.WebSocketCall;
+import com.squareup.okhttp.ws.WebSocketListener;
+import com.taobao.weex.appfram.websocket.IWebSocketAdapter;
+import com.taobao.weex.appfram.websocket.WebSocketCloseCodes;
+import com.taobao.weex.http.Status;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import okio.Buffer;
+import okio.BufferedSource;
+
+/**
+ * Created by moxun on 16/12/27.
+ */
+
+public class DefaultWebSocketAdapter implements IWebSocketAdapter {
+
+    private WebSocket ws;
+    private EventListener eventListener;
+    private WSEventReporter wsEventReporter;
+
+    @Override
+    public void connect(String url, @Nullable final String protocol, EventListener listener) {
+        this.eventListener = listener;
+        this.wsEventReporter = WSEventReporter.newInstance();
+        OkHttpClient okHttpClient = new OkHttpClient();
+
+        Request.Builder builder = new Request.Builder();
+
+        if (protocol != null) {
+            builder.addHeader(HEADER_SEC_WEBSOCKET_PROTOCOL, protocol);
+        }
+
+        builder.url(url);
+        wsEventReporter.created(url);
+
+        Request wsRequest = builder.build();
+        WebSocketCall webSocketCall = WebSocketCall.create(okHttpClient, wsRequest);
+
+        try {
+            Field field = WebSocketCall.class.getDeclaredField("request");
+            field.setAccessible(true);
+            Request realRequest = (Request) field.get(webSocketCall);
+            Headers wsHeaders = realRequest.headers();
+            Map<String, String> headers = new HashMap<>();
+            for (String name : wsHeaders.names()) {
+                headers.put(name, wsHeaders.values(name).toString());
+            }
+            wsEventReporter.willSendHandshakeRequest(headers, null);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+        webSocketCall.enqueue(new WebSocketListener() {
+            @Override
+            public void onOpen(WebSocket webSocket, Request request, Response response) throws IOException {
+                ws = webSocket;
+                eventListener.onOpen();
+                Headers wsHeaders = response.headers();
+                Map<String, String> headers = new HashMap<>();
+                for (String name : wsHeaders.names()) {
+                    headers.put(name, wsHeaders.values(name).toString());
+                }
+                wsEventReporter.handshakeResponseReceived(response.code(),
+                        Status.getStatusText(String.valueOf(response.code())),
+                        headers);
+            }
+
+            @Override
+            public void onMessage(BufferedSource payload, WebSocket.PayloadType type) throws IOException {
+                if (type == WebSocket.PayloadType.BINARY) {
+                    wsEventReporter.frameReceived(payload.readByteArray());
+                } else {
+                    String message = payload.readUtf8();
+                    eventListener.onMessage(message);
+                    wsEventReporter.frameReceived(message);
+                }
+                payload.close();
+            }
+
+            @Override
+            public void onPong(Buffer payload) {
+
+            }
+
+            @Override
+            public void onClose(int code, String reason) {
+                eventListener.onClose(code, reason, true);
+                wsEventReporter.closed();
+            }
+
+            @Override
+            public void onFailure(IOException e) {
+                e.printStackTrace();
+                if (e instanceof EOFException) {
+                    eventListener.onClose(WebSocketCloseCodes.CLOSE_NORMAL.getCode(), WebSocketCloseCodes.CLOSE_NORMAL.name(), true);
+                    wsEventReporter.closed();
+                } else {
+                    eventListener.onError(e.getMessage());
+                    wsEventReporter.frameError(e.getMessage());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void send(String data) {
+        if (ws != null) {
+            try {
+                Buffer buffer = new Buffer().writeUtf8(data);
+                ws.sendMessage(WebSocket.PayloadType.TEXT, buffer.buffer());
+                buffer.flush();
+                buffer.close();
+
+                wsEventReporter.frameSent(data);
+            } catch (Exception e) {
+                e.printStackTrace();
+                reportError(e.getMessage());
+                wsEventReporter.frameError(e.getMessage());
+            }
+        } else {
+            reportError("WebSocket is not ready");
+        }
+    }
+
+    @Override
+    public void close(int code, String reason) {
+        if (ws != null) {
+            try {
+                ws.close(code, reason);
+            } catch (Exception e) {
+                e.printStackTrace();
+                reportError(e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+        if (ws != null) {
+            try {
+                ws.close(WebSocketCloseCodes.CLOSE_GOING_AWAY.getCode(), WebSocketCloseCodes.CLOSE_GOING_AWAY.name());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void reportError(String message) {
+        if (eventListener != null) {
+            eventListener.onError(message);
+        }
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapterFactory.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapterFactory.java
new file mode 100644
index 0000000..a2cd4ec
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/DefaultWebSocketAdapterFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import com.taobao.weex.appfram.websocket.IWebSocketAdapter;
+import com.taobao.weex.appfram.websocket.IWebSocketAdapterFactory;
+
+/**
+ * Created by moxun on 16/12/28.
+ */
+
+public class DefaultWebSocketAdapterFactory implements IWebSocketAdapterFactory {
+    @Override
+    public IWebSocketAdapter createWebSocketAdapter() {
+        return new DefaultWebSocketAdapter();
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageAdapter.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageAdapter.java
new file mode 100644
index 0000000..bc4ab42
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageAdapter.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.graphics.Color;
+import android.graphics.drawable.Animatable;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.ImageView;
+
+import com.facebook.common.executors.UiThreadImmediateExecutorService;
+import com.facebook.common.internal.Preconditions;
+import com.facebook.common.logging.FLog;
+import com.facebook.common.references.CloseableReference;
+import com.facebook.datasource.BaseDataSubscriber;
+import com.facebook.datasource.DataSource;
+import com.facebook.datasource.DataSubscriber;
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.controller.BaseControllerListener;
+import com.facebook.drawee.controller.ControllerListener;
+import com.facebook.drawee.interfaces.DraweeController;
+import com.facebook.drawee.view.DraweeView;
+import com.facebook.imagepipeline.common.ImageDecodeOptions;
+import com.facebook.imagepipeline.core.ImagePipeline;
+import com.facebook.imagepipeline.image.CloseableImage;
+import com.facebook.imagepipeline.image.CloseableStaticBitmap;
+import com.facebook.imagepipeline.image.ImageInfo;
+import com.facebook.imagepipeline.image.QualityInfo;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+import com.taobao.weex.WXSDKManager;
+import com.taobao.weex.adapter.IWXImgLoaderAdapter;
+import com.taobao.weex.common.WXImageStrategy;
+import com.taobao.weex.dom.WXImageQuality;
+
+public class FrescoImageAdapter implements IWXImgLoaderAdapter {
+
+    public FrescoImageAdapter() {
+    }
+
+    @Override
+    public void setImage(final String url, final ImageView view,
+                         WXImageQuality quality, WXImageStrategy strategy) {
+
+        WXSDKManager.getInstance().postOnUiThread(new Runnable() {
+
+            @Override
+            public void run() {
+                if (view == null || view.getLayoutParams() == null) {
+                    return;
+                }
+                if (TextUtils.isEmpty(url)) {
+                    view.setImageBitmap(null);
+                    return;
+                }
+                String temp = url;
+                if (url.startsWith("//")) {
+                    temp = "http:" + url;
+                }
+                if (view.getLayoutParams().width <= 0 || view.getLayoutParams().height <= 0) {
+                    return;
+                }
+
+                Uri uri = Uri.parse(temp);
+
+                ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()
+                        .setBackgroundColor(Color.GREEN)
+                        .build();
+
+                ImageRequest request = ImageRequestBuilder
+                        .newBuilderWithSource(uri)
+                        .setImageDecodeOptions(decodeOptions)
+                        .setAutoRotateEnabled(true)
+                        .setLocalThumbnailPreviewsEnabled(true)
+                        .setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
+                        .setProgressiveRenderingEnabled(false)
+                        .build();
+
+                if(view instanceof DraweeView){
+                    Log.d("FrescoImageAdapter","load: "+url);
+                    ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
+                        @Override
+                        public void onFinalImageSet(
+                            String id,
+                            @Nullable ImageInfo imageInfo,
+                            @Nullable Animatable anim) {
+                            if (imageInfo == null) {
+                                return;
+                            }
+                            QualityInfo qualityInfo = imageInfo.getQualityInfo();
+                            FLog.d("Final image received! " +
+                                    "Size %d x %d",
+                                "Quality level %d, good enough: %s, full quality: %s",
+                                imageInfo.getWidth(),
+                                imageInfo.getHeight(),
+                                qualityInfo.getQuality(),
+                                qualityInfo.isOfGoodEnoughQuality(),
+                                qualityInfo.isOfFullQuality());
+                        }
+
+                        @Override
+                        public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
+                            FLog.d("","Intermediate image received");
+                        }
+
+                        @Override
+                        public void onFailure(String id, Throwable throwable) {
+                            FLog.e(getClass(), throwable, "Error loading %s", id);
+                        }
+                    };
+                    DraweeController controller = Fresco.newDraweeControllerBuilder()
+                        .setAutoPlayAnimations(true)
+                        .setControllerListener(controllerListener)
+                        .setUri(uri)
+                        .setImageRequest(request)
+                        .build();
+                    ((DraweeView)view).setController(controller);
+
+                }else {
+                    ImagePipeline imagePipeline = Fresco.getImagePipeline();
+                    DataSource<CloseableReference<CloseableImage>>
+                        dataSource = imagePipeline.fetchDecodedImage(request, new Object());
+                    DataSubscriber dataSubscriber =
+                        new BaseDataSubscriber<CloseableReference<CloseableImage>>() {
+                            @Override
+                            public void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
+
+                                CloseableReference<CloseableImage> imageReference = dataSource.getResult();
+                                if (imageReference != null) {
+                                    try {
+                                        // do something with the image
+                                        Preconditions.checkState(CloseableReference.isValid(imageReference));
+                                        CloseableImage closeableImage = imageReference.get();
+                                        if (closeableImage instanceof CloseableStaticBitmap) {
+                                            CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) closeableImage;
+                                            view.setImageBitmap(closeableStaticBitmap.getUnderlyingBitmap());
+                                            // boolean hasResult =  null != closeableStaticBitmap.getUnderlyingBitmap();
+                                        } else {
+                                            throw new UnsupportedOperationException("Unrecognized image class: " + closeableImage);
+                                        }
+                                    } finally {
+                                        imageReference.close();
+                                    }
+                                }
+                            }
+
+                            @Override
+                            public void onFailureImpl(DataSource dataSource) {
+                            }
+                        };
+
+                    dataSource.subscribe(dataSubscriber, UiThreadImmediateExecutorService.getInstance());
+                }
+            }
+        }, 0);
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageComponent.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageComponent.java
new file mode 100644
index 0000000..56e9a25
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageComponent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.widget.ImageView;
+import com.taobao.weex.WXSDKInstance;
+import com.taobao.weex.ui.action.BasicComponentData;
+import com.taobao.weex.ui.component.WXImage;
+import com.taobao.weex.ui.component.WXVContainer;
+
+/**
+ * Created by sospartan on 8/19/16.
+ */
+public class FrescoImageComponent extends WXImage {
+
+  public FrescoImageComponent(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) {
+    super(instance, parent, basicComponentData);
+  }
+
+  @Override
+  protected ImageView initComponentHostView(@NonNull Context context) {
+    FrescoImageView view = new FrescoImageView(context);
+    view.setScaleType(ImageView.ScaleType.FIT_XY);
+
+    return view;
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageView.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageView.java
new file mode 100644
index 0000000..9227a90
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/FrescoImageView.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.content.Context;
+import android.graphics.*;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import com.facebook.drawee.view.DraweeView;
+import com.facebook.drawee.view.SimpleDraweeView;
+import com.taobao.weex.ui.view.border.BorderDrawable;
+import com.taobao.weex.ui.view.gesture.WXGesture;
+import com.taobao.weex.ui.view.gesture.WXGestureObservable;
+import com.taobao.weex.utils.WXViewUtils;
+
+/**
+ * Created by sospartan on 8/19/16.
+ */
+public class FrescoImageView extends SimpleDraweeView implements WXGestureObservable {
+  public FrescoImageView(Context context) {
+    super(context);
+  }
+
+  public FrescoImageView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  public FrescoImageView(Context context, AttributeSet attrs, int defStyle) {
+    super(context, attrs, defStyle);
+  }
+
+  public FrescoImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    super(context, attrs, defStyleAttr, defStyleRes);
+  }
+
+  private WXGesture wxGesture;
+
+  @Override
+  public void registerGestureListener(WXGesture wxGesture) {
+    this.wxGesture = wxGesture;
+  }
+
+  @Override
+  public WXGesture getGestureListener() {
+    return wxGesture;
+  }
+
+  @Override
+  public boolean onTouchEvent(MotionEvent event) {
+    boolean result = super.onTouchEvent(event);
+    if (wxGesture != null) {
+      result |= wxGesture.onTouch(this, event);
+    }
+    return result;
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/ImageAdapter.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/ImageAdapter.java
new file mode 100644
index 0000000..77dc4fc
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/ImageAdapter.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.net.Uri;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.widget.ImageView;
+
+import com.squareup.picasso.Callback;
+import com.squareup.picasso.Picasso;
+import com.taobao.weex.WXEnvironment;
+import com.taobao.weex.WXSDKInstance;
+import com.taobao.weex.WXSDKManager;
+import com.taobao.weex.adapter.IWXImgLoaderAdapter;
+import com.taobao.weex.common.WXImageStrategy;
+import com.taobao.weex.dom.WXImageQuality;
+import com.taobao.weex.ui.IFComponentHolder;
+
+public class ImageAdapter implements IWXImgLoaderAdapter {
+
+  public ImageAdapter() {
+  }
+
+  @Override
+  public void setImage(final String url, final ImageView view,
+                       WXImageQuality quality, final WXImageStrategy strategy) {
+    Runnable runnable = new Runnable() {
+
+      @Override
+      public void run() {
+        if(view==null||view.getLayoutParams()==null){
+          return;
+        }
+        if (TextUtils.isEmpty(url)) {
+          view.setImageBitmap(null);
+          return;
+        }
+        if (null != strategy){
+          recordImgLoadAction(strategy.instanceId);
+        }
+
+        String temp = url;
+        if (url.startsWith("//")) {
+          temp = "http:" + url;
+        }
+
+        if(!TextUtils.isEmpty(strategy.placeHolder)){
+          Picasso.Builder builder=new Picasso.Builder(WXEnvironment.getApplication());
+          Picasso picasso=builder.build();
+          picasso.load(Uri.parse(strategy.placeHolder)).into(view);
+
+          view.setTag(strategy.placeHolder.hashCode(),picasso);
+        }
+
+        Picasso.with(WXEnvironment.getApplication())
+                .load(temp)
+                .transform(new BlurTransformation(strategy.blurRadius))
+                .into(view, new Callback() {
+                  @Override
+                  public void onSuccess() {
+                    if(strategy.getImageListener()!=null){
+                      strategy.getImageListener().onImageFinish(url,view,true,null);
+                    }
+                    recordImgLoadResult(strategy.instanceId,true,null);
+
+                    if(!TextUtils.isEmpty(strategy.placeHolder)){
+                      ((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
+                    }
+                  }
+
+                  @Override
+                  public void onError() {
+                    if(strategy.getImageListener()!=null){
+                      strategy.getImageListener().onImageFinish(url,view,false,null);
+                    }
+                    recordImgLoadResult(strategy.instanceId,false,null);
+                  }
+                });
+      }
+    };
+    if(Thread.currentThread() == Looper.getMainLooper().getThread()){
+      runnable.run();
+    }else {
+      WXSDKManager.getInstance().postOnUiThread(runnable, 0);
+    }
+  }
+  private void recordImgLoadAction(String instanceId){
+    WXSDKInstance instance = WXSDKManager.getInstance().getAllInstanceMap().get(instanceId);
+    if (null == instance || instance.isDestroy()){
+      return;
+    }
+    instance.getApmForInstance().actionLoadImg();
+  }
+
+  private void recordImgLoadResult(String instanceId,boolean succeed,String errorCode){
+    WXSDKInstance instance = WXSDKManager.getInstance().getAllInstanceMap().get(instanceId);
+    if (null == instance || instance.isDestroy()){
+      return;
+    }
+    instance.getApmForInstance().actionLoadImgResult(succeed,errorCode);
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/JSExceptionAdapter.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/JSExceptionAdapter.java
new file mode 100644
index 0000000..3c6c2ff
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/JSExceptionAdapter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import com.taobao.weex.WXEnvironment;
+import com.taobao.weex.adapter.IWXJSExceptionAdapter;
+import com.taobao.weex.common.WXJSExceptionInfo;
+import com.taobao.weex.utils.WXLogUtils;
+
+/**
+ */
+
+public class JSExceptionAdapter implements IWXJSExceptionAdapter {
+
+  @Override
+  public void onJSException(WXJSExceptionInfo exception) {
+    if (exception != null && WXEnvironment.isApkDebugable()) {
+      WXLogUtils.d(exception.toString());
+    }
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/adapter/PicassoBasedDrawableLoader.java b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/PicassoBasedDrawableLoader.java
new file mode 100644
index 0000000..a642dc7
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/adapter/PicassoBasedDrawableLoader.java
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.adapter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+import com.taobao.weex.WXSDKManager;
+import com.taobao.weex.adapter.DrawableStrategy;
+import com.taobao.weex.adapter.IDrawableLoader;
+
+public class PicassoBasedDrawableLoader implements IDrawableLoader {
+
+  private Context mContext;
+
+  public PicassoBasedDrawableLoader(Context context) {
+    mContext = context;
+  }
+
+  @Override
+  public void setDrawable(final String url,
+                          final DrawableTarget drawableTarget,
+                          final DrawableStrategy drawableStrategy) {
+    WXSDKManager.getInstance().postOnUiThread(new Runnable() {
+      @Override
+      public void run() {
+        String temp = url;
+        if (url.startsWith("//")) {
+          temp = "http:" + url;
+        }
+
+        /** This is a hack for picasso, as Picasso hold weakReference to Target.
+         * http://stackoverflow.com/questions/24180805/onbitmaploaded-of-target-object-not-called-on-first-load
+         */
+        class PlaceHolderDrawableTarget extends Drawable implements Target {
+
+          @Override
+          public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+            BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
+            bitmapDrawable.setGravity(Gravity.FILL);
+            drawableTarget.setDrawable(bitmapDrawable, true);
+          }
+
+          @Override
+          public void onBitmapFailed(Drawable errorDrawable) {
+
+          }
+
+          @Override
+          public void onPrepareLoad(Drawable placeHolderDrawable) {
+            drawableTarget.setDrawable(this, true);
+          }
+
+          @Override
+          public void draw(Canvas canvas) {
+
+          }
+
+          @Override
+          public void setAlpha(int alpha) {
+
+          }
+
+          @Override
+          public void setColorFilter(ColorFilter colorFilter) {
+
+          }
+
+          @Override
+          public int getOpacity() {
+            return PixelFormat.UNKNOWN;
+          }
+        }
+        Picasso.
+                with(mContext).
+                load(temp).
+                resize(drawableStrategy.width, drawableStrategy.height).
+                onlyScaleDown().
+                into(new PlaceHolderDrawableTarget());
+      }
+    }, 0);
+
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/util/AssertUtil.java b/android/commons/src/main/java/com/alibaba/weex/commons/util/AssertUtil.java
new file mode 100644
index 0000000..07ec3b9
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/util/AssertUtil.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.util;
+
+/**
+ * Created by sospartan on 5/31/16.
+ */
+public class AssertUtil {
+  public static<T extends Exception> void throwIfNull(Object object,T e) throws T {
+    if(object == null){
+      throw e;
+    }
+  }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/util/RequestIdGenerator.java b/android/commons/src/main/java/com/alibaba/weex/commons/util/RequestIdGenerator.java
new file mode 100644
index 0000000..8f4f169
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/util/RequestIdGenerator.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Created by moxun on 17/4/20.
+ */
+
+public class RequestIdGenerator {
+    private static final AtomicInteger sIdGenerator = new AtomicInteger(0);
+
+    public static int nextRequestId() {
+        return sIdGenerator.getAndIncrement();
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/util/ScreenUtil.java b/android/commons/src/main/java/com/alibaba/weex/commons/util/ScreenUtil.java
new file mode 100644
index 0000000..e2caed3
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/util/ScreenUtil.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.util;
+
+import android.content.res.TypedArray;
+import android.graphics.Point;
+import android.os.Build;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class ScreenUtil {
+    private static final String TAG = "WXTBUtil";
+
+    private static boolean isSupportSmartBar = false;
+
+    static {
+        isSupportSmartBar = isSupportSmartBar();
+    }
+    public static int getDisplayWidth(AppCompatActivity activity){
+        int width=0;
+        if (activity != null && activity.getWindowManager() != null && activity.getWindowManager().getDefaultDisplay() != null) {
+            Point point=new Point();
+            activity.getWindowManager().getDefaultDisplay().getSize(point);
+            width = point.x;
+        }
+        return width;
+    }
+
+    public static int getDisplayHeight(AppCompatActivity activity) {
+        int height = 0;
+        if (activity != null && activity.getWindowManager() != null && activity.getWindowManager().getDefaultDisplay() != null) {
+            Point point=new Point();
+            activity.getWindowManager().getDefaultDisplay().getSize(point);
+            height=point.y;
+        }
+
+        Log.e(TAG, "isSupportSmartBar:" + isSupportSmartBar);
+
+        if (isSupportSmartBar) {
+            int smartBarHeight = getSmartBarHeight(activity);
+            Log.e(TAG, "smartBarHeight:" + smartBarHeight);
+            height -= smartBarHeight;
+        }
+
+        if (activity.getSupportActionBar() != null) {
+          int actionbar= activity.getSupportActionBar().getHeight();
+          if(actionbar==0){
+            TypedArray actionbarSizeTypedArray=activity.obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
+            actionbar= (int) actionbarSizeTypedArray.getDimension(0,0);
+          }
+          Log.d(TAG, "actionbar:" + actionbar);
+          height -= actionbar;
+        }
+
+        int status = getStatusBarHeight(activity);
+        Log.d(TAG, "status:" + status);
+
+        height -= status;
+
+        Log.d(TAG,"height:"+height);
+        return height;
+    }
+
+    private static int getStatusBarHeight(AppCompatActivity activity) {
+        Class<?> c;
+        Object obj;
+        Field field;
+        int x;
+        int statusBarHeight = 0;
+        try {
+            c = Class.forName("com.android.internal.R$dimen");
+            obj = c.newInstance();
+            field = c.getField("status_bar_height");
+            x = Integer.parseInt(field.get(obj).toString());
+            statusBarHeight = activity.getResources().getDimensionPixelSize(x);
+        } catch (Exception e1) {
+            e1.printStackTrace();
+        }
+        return statusBarHeight;
+    }
+
+    private static int getSmartBarHeight(AppCompatActivity activity) {
+        ActionBar actionbar = activity.getSupportActionBar();
+        if (actionbar != null)
+            try {
+                Class c = Class.forName("com.android.internal.R$dimen");
+                Object obj = c.newInstance();
+                Field field = c.getField("mz_action_button_min_height");
+                int height = Integer.parseInt(field.get(obj).toString());
+                return activity.getResources().getDimensionPixelSize(height);
+            } catch (Exception e) {
+                e.printStackTrace();
+                actionbar.getHeight();
+            }
+        return 0;
+    }
+
+    private static boolean isSupportSmartBar() {
+        try {
+            final Method method = Build.class.getMethod("hasSmartBar");
+            return method != null;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/android/commons/src/main/java/com/alibaba/weex/commons/util/WSEventReporter.java b/android/commons/src/main/java/com/alibaba/weex/commons/util/WSEventReporter.java
new file mode 100644
index 0000000..aba09c1
--- /dev/null
+++ b/android/commons/src/main/java/com/alibaba/weex/commons/util/WSEventReporter.java
@@ -0,0 +1,210 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.alibaba.weex.commons.util;
+
+import android.support.annotation.Nullable;
+import android.util.Pair;
+
+import com.taobao.weex.devtools.inspector.network.NetworkEventReporter;
+import com.taobao.weex.devtools.inspector.network.NetworkEventReporterManager;
+import com.taobao.weex.devtools.inspector.network.SimpleBinaryInspectorWebSocketFrame;
+import com.taobao.weex.devtools.inspector.network.SimpleTextInspectorWebSocketFrame;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by moxun on 17/4/20.
+ */
+
+public class WSEventReporter {
+
+    private String requestId;
+    private NetworkEventReporter reporter;
+    private WSRequest request;
+
+    public static WSEventReporter newInstance() {
+        return new WSEventReporter();
+    }
+
+    private WSEventReporter() {
+        requestId = String.valueOf(RequestIdGenerator.nextRequestId());
+        reporter = NetworkEventReporterManager.get();
+    }
+
+    public void created(String url) {
+        if (reporter != null) {
+            reporter.webSocketCreated(requestId, url);
+        }
+    }
+
+    public void closed() {
+        if (reporter != null) {
+            reporter.webSocketClosed(requestId);
+        }
+    }
+
+    public void willSendHandshakeRequest(Map<String, String> headers, @Nullable String friendlyName) {
+        if (reporter != null) {
+            request = new WSRequest(requestId, friendlyName, headers);
+            reporter.webSocketWillSendHandshakeRequest(request);
+        }
+    }
+
+    public void handshakeResponseReceived(int code, String phrase, Map<String, String> responseHeaders) {
+        if (reporter != null) {
+            reporter.webSocketHandshakeResponseReceived(new WSResponse(requestId, code, phrase, responseHeaders, request));
+        }
+    }
+
+    public void frameSent(String frame) {
+        if (reporter != null) {
+            reporter.webSocketFrameSent(new SimpleTextInspectorWebSocketFrame(requestId, frame));
+        }
+    }
+
+    public void frameSent(byte[] frame) {
+        if (reporter != null) {
+            reporter.webSocketFrameSent(new SimpleBinaryInspectorWebSocketFrame(requestId, frame));
+        }
+    }
+
+    public void frameReceived(String frame) {
+        if (reporter != null) {
+            reporter.webSocketFrameReceived(new SimpleTextInspectorWebSocketFrame(requestId, frame));
+        }
+    }
+
+    public void frameReceived(byte[] frame) {
+        if (reporter != null) {
+            reporter.webSocketFrameReceived(new SimpleBinaryInspectorWebSocketFrame(requestId, frame));
+        }
+    }
+
+    public void frameError(String msg) {
+        if (reporter != null) {
+            reporter.webSocketFrameError(requestId, msg);
+        }
+    }
+
+    private static class WSRequest extends WSHeaderCommon implements NetworkEventReporter.InspectorWebSocketRequest {
+
+        private String id;
+        private String name;
+
+        public WSRequest(String id, String name, Map<String, String> headers) {
+            attachHeaders(headers);
+            this.id = id;
+            this.name = name;
+            if (this.name == null) {
+                this.name = "WS Connection " + id;
+            }
+        }
+
+        @Override
+        public String id() {
+            return id;
+        }
+
+        @Override
+        public String friendlyName() {
+            return name;
+        }
+    }
+
+    private static class WSResponse extends WSHeaderCommon implements NetworkEventReporter.InspectorWebSocketResponse {
+
+        private String id;
+        private int code;
+        private String phrase;
+        private WSHeaderCommon headers;
+
+        public WSResponse(String id, int code, String phrase, Map<String, String> responseHeaders, WSHeaderCommon headers) {
+            attachHeaders(responseHeaders);
+            this.id = id;
+            this.code = code;
+            this.phrase = phrase;
+            this.headers = headers;
+        }
+
+        @Override
+        public String requestId() {
+            return id;
+        }
+
+        @Override
+        public int statusCode() {
+            return code;
+        }
+
+        @Override
+        public String reasonPhrase() {
+            return phrase;
+        }
+
+        @Nullable
+        @Override
+        public NetworkEventReporter.InspectorHeaders requestHeaders() {
+            return headers;
+        }
+    }
+
+    private static class WSHeaderCommon implements NetworkEventReporter.InspectorHeaders {
+
+        private List<Pair<String, String>> headerList = new ArrayList<>();
+
+        public void attachHeaders(Map<String, String> headers) {
+            for (Map.Entry<String, String> entry : headers.entrySet()) {
+                headerList.add(new Pair<>(entry.getKey(), entry.getValue()));
+            }
+        }
+
+        public void addHeader(String key, String value) {
+            headerList.add(new Pair<>(key, value));
+        }
+
+
+        @Override
+        public int headerCount() {
+            return headerList.size();
+        }
+
+        @Override
+        public String headerName(int index) {
+            return headerList.get(index).first;
+        }
+
+        @Override
+        public String headerValue(int index) {
+            return headerList.get(index).second;
+        }
+
+        @Nullable
+        @Override
+        public String firstHeaderValue(String name) {
+            for (Pair<String, String> pair : headerList) {
+                if (pair.first.equals(name)) {
+                    return pair.second;
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/android/commons/src/main/res/values/strings.xml b/android/commons/src/main/res/values/strings.xml
new file mode 100644
index 0000000..be8deae
--- /dev/null
+++ b/android/commons/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<resources>
+    <string name="app_name">My Module</string>
+</resources>