2013年7月31日 星期三

Define class in JS

http://www.phpied.com/3-ways-to-define-a-javascript-class/

Use Regex to match a string in Javascript

I use the following line to capture a JS link in a string:

var url = req.responseText.match(/http:.*js/);

For more information and usage, find the reference links for deftails.

Reference links
http://stackoverflow.com/questions/1707299/how-to-extract-a-string-using-javascript-regex
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_match_regexp

difference between using test() and match() in JS

They are both matching methods but the difference mainly lies on the result you want and the performance. First, test() returns true/false to if a match is found, on the other hand, match() returns the matching string. Secondly, test() has a better performance. Use them wisely.

Reference links
http://stackoverflow.com/questions/10940137/regex-test-v-s-string-match-to-know-if-a-string-matches-a-regular-expression

online JS beautifier

A great website which provides an online tool to help organize your messed-up JS content. Simply copy and paste, enjoy it !

http://jsbeautifier.org/

Including JS script tag src correctly with custom User-Agent words by changing it in WebView setting

My code is as follow:

        myWebView = new WebView(this);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        myWebView.getSettings().setUserAgentString("iTunes-AppleTV/5.1 (3; 8GB; dt:12)");
        System.out.println(android.os.Build.VERSION.SDK_INT);
        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
        }
        myWebView.setWebChromeClient(new WebChromeClient());
      
        js_url_string =
                " var script=document.createElement('script'); "
                + " script.setAttribute('type','text/javascript'); "
                + " script.setAttribute('src', 'http://trailers.apple.com/appletv/us/js/application.js'); "
                + " script.onload = function() { "
                + " var req = new XMLHttpRequest(); "
                + " var req_cnt = 0; "
                + " req.onreadystatechange = function() { "
                + "       try { "
                + "        gtkTV.dump(req.readyState); "
                + "        "
                + "           if(req.readyState == 4) { "
                + "            if(req.status == 200) { "
                + "                if (req_cnt == 1) {"
                + "                    var url = req.responseText.match(/http:.*js/); "
                + "                    req.open('GET', url, true); "
                + "                    req.send(null); "
                + "                    req_cnt += 1; "
                + "                } "
                + "                else if (req_cnt == 2) { "
                + "                       gtkTV.loadXML(gtkTV.parseXML(req.responseText)); "
                + "                } "
                + "                else { "
                + "                    gtkTV.dump('Unknown request'); "
                + "                } "
                + "            } "
                + "            else { "
                + "                gtkTV.dump(req.statusText); "
                + "            } "
                + "        } "
                + "    } "
                + "       catch(e){ "
                + "           req.abort(); "
                + "        gtkTV.dump('Loadpage error!'); "
                + "       } "
                + " }; "
                + " gtkTV.dump(atv.config.ROOT_URL); "
                + " req.open('GET', atv.config.ROOT_URL, true); "
                + " req.send(null); "
                + " req_cnt += 1; "
                + " }; "
                + " document.getElementsByTagName('head')[0].appendChild(script); ";
      
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.getElementByTagName = function(id) {"
                        + "     return document.getElementsByTagName(id);"
                        + " };"
                        + js_url_string
                        + "})()");
            }
        });
        myWebView.addJavascriptInterface(new MyAndroidJSInterface(this, MainActivity.class), "gtkTV");
        myWebView.loadUrl("file:///android_asset/tmp.html");


My goal was to make script tag work for src to send request and include an external JS with a custom User-Agent value. Unfortunately after I did some research on the internet, I could not find a solution for it and people always say it's not a bug but not allowed by FF. At the moment when I was planning to give up and tried some other ugly ways to do that, I found a post at the reference link and realized Damn! that I was using WebView and there are some methods I could use to do that ! Better late than never, ugh.

Reference links
http://developer.android.com/guide/webapps/webview.html

get unrecognized content from httpresponse (how to handle gzipped data)

While using httpClient to retrieve content from a HTTP response, I got a content that I can not recognized and it looked like a bunch of compressed data. I figured that it might be due to the content encoding and the following is a code snippet that I used to solve this issue:

private String gzipIS2String(InputStream is) {
        String result = "";
        String line="";
       
        try {
            GZIPInputStream  gzipIS = new GZIPInputStream(is);
            Reader          decoder    = new InputStreamReader (gzipIS, "UTF-8");
            BufferedReader  buffered   = new BufferedReader    (decoder);
       
            while ((line = buffered.readLine()) != null) {
                result += line;
            }
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        return result;
    }


public String getURL(String url) {
        String result = null;

        try {
            DefaultHttpClient httpClient = new DefaultHttpClient();
            httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "iTunes-AppleTV/5.1 (3; 8GB; dt:12)");
            HttpGet httpGet = new HttpGet(url);

            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity.getContentEncoding().toString().contains("gzip")) {
                result = gzipIS2String(httpEntity.getContent());
            }
            else {
                result = EntityUtils.toString(httpEntity);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }



Reference links
http://stackoverflow.com/questions/13866253/gzip-decompressing-returns-random-characters
http://stackoverflow.com/questions/2474193/uncompress-gziped-http-response-in-java
http://stackoverflow.com/questions/15040504/how-to-converting-bufferedreader-to-string-at-once
http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpEntity.html

timing to use setRequestHeader

interface XMLHttpRequest {
           attribute EventListener   onreadystatechange;
  readonly attribute unsigned short  readyState;
  void               open(in DOMString method, in DOMString url);
  void               open(in DOMString method, in DOMString url, in boolean async);
  void               open(in DOMString method, in DOMString url, in boolean async, in DOMString user);
  void               open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password);
  void               setRequestHeader(in DOMString header, in DOMString value);
  void               send();
  void               send(in DOMString data);
  void               send(in Document data);
  void               abort();
  DOMString          getAllResponseHeaders();
  DOMString          getResponseHeader(in DOMString header);
  readonly attribute DOMString       responseText;
  readonly attribute Document        responseXML;
  readonly attribute unsigned short  status;
  readonly attribute DOMString       statusText;
};

(1) setRequestHeader() has to be used after request.open(...), where request is a instance of XMLHttpRequest
(2) Setting User-Agent is forbidden by FF as well as some of the other special header fields.
(3) If you're doing things on Android, changing User-Agent through WebView configuration is possible if you're attempting to use WebView to execute some Javascript code and want the request to be sent with a custom User-Agent.

Reference links
http://stackoverflow.com/questions/581383/adding-custom-http-headers-using-javascript

http://stackoverflow.com/questions/2357430/invalid-state-err-dom-exception-11
http://www.w3.org/TR/2007/WD-XMLHttpRequest-20070227/#xmlhttprequest-members 
http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method

2013年7月30日 星期二

Hack http header

The idea is simple. Find right APIs and use them to change or add HTTP header fields for specific purpose.

Like in Android:

            DefaultHttpClient httpClient = new DefaultHttpClient();
            httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "iTunes-AppleTV/5.1 (3; 8GB; dt:12)");
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("X-Apple-Store-Front", "143463-18,18A");
            httpPost.setHeader("X-Apple-TV-Resolution", "1080");
            httpPost.setHeader("X-Apple-TV-Version", "5.1");


            HttpResponse httpResponse = httpClient.execute(httpPost);


The problems lie on Javascript script tag. It's much easier to do this for XMLHttpRequest() because there're some corresponding APIS to use from, but when it comes to script tag, I can't find a solution online for this as to now.

Reference links
http://stackoverflow.com/questions/7092252/how-to-set-header-using-http-post-method
http://stackoverflow.com/questions/2533236/setting-user-agent-in-java-httpclient-and-allow-redirects-to-true
http://stackoverflow.com/questions/4866722/is-it-possible-to-set-custom-headers-on-js-script-requests

2013年7月28日 星期日

new JS2ANRD interface for javascript-only-semantics coding within a JS file

The JS files provided by the server use custom client interface that is beyond what Android can support, such as function pointer assignment and accessing array using string index. Thus an additional interface has to be included to form the following Android<->Javascript communication:

                                             Client                                             |                Server
local Android interface <---> local custom Javascript interface <---> remote Javascript code
        (gtkTV)                                            (atv)                                     

The following is the sample code:

[local tmp.html]

<HTML>
<head>
<script>
function atvclass() {
    this.loadXML = function(doc) {
        gtkTV.loadXML(doc);
    };
    this.parseXML = function(xml) {
        return gtkTV.parseXML(xml);
    };
    this.sessionStorage = new Array();
}

var atv = new atvclass();
</script>
</head>
<body>
</body>
</HTML>


[executing JS function]
...
       WebView myWebView = new WebView(this);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        System.out.println(android.os.Build.VERSION.SDK_INT);
        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
        }
        myWebView.setWebChromeClient(new WebChromeClient());
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.getElementByTagName = function(id) {"
                        + "     return document.getElementsByTagName(id);"
                        + " };"
                        + js_url_string
<--composed of creating script nodes to include multiple JS files
                        + "})()");
            }
        });
        myWebView.addJavascriptInterface(new MyAndroidJSInterface(this, SecondActivity.class), "gtkTV");
        myWebView.loadUrl("file:///android_asset/tmp.html");


[sample server JS code]

...
atv.Element.prototype.getElementByTagName = function(name)
    {
        var elements = this.getElementsByTagName(name);
        return(elements && elements.length > 0) ? elements[0] : undefined;
    };

...
atv.sessionStorage['srtlist'] = srtlist;
atv.sessionStorage['filelist'] = filelist;
...

Reference links
http://www.phpied.com/3-ways-to-define-a-javascript-class/

function pointer usage in Android/Java

Java does not support function pointer assignment, but there's an alternative to achieve the similar goal:

interface JS2ANRD_Prototype {
        public Element getElementById();
        public Element getElementsByTagName();

}

 kkk(new JS2ANRD_Prototype() {
            public Element getElementById() {
                return (Element)null;
            }
            public Element getElementsByTagName() {
                return (Element)null;
            }
        });


public void kkk(JS2ANRD_Prototype p) {...}

Reference links
http://www.ozzysun.com/2011/07/androidcallback.html
http://stackoverflow.com/questions/1073358/function-pointers-in-java

include js files and use them as one js lib

Take a  look at the following html part:

<html>
<head>
<script src="file:\\\android_assets\1st.js"></script>
<script src="file:\\\android_assets\2nd.js"></script>
<script src="file:\\\android_assets\3rd.js"></script>
</head>
</html>

Javascript runtime will sequentially load in JS files, so say if you wanna call a JS function which is part of one of these three JS files but you don't know which one it is, just put the calling operation at the end, or use onload attribute for the last script inclusion, such as:

<html>
<head>
<script src="file:\\\android_assets\1.js"></script>
<script src="file:\\\android_assets\2.js"></script>
<script src="file:\\\android_assets\3.js" onload="function() {target_js_func();}" ></script>  <-method 1
<script>
target_js_func();  <---method 2
</script>
</head>

</html>

This way you won't get undefined error and will get the function working properly.

Edited:
Please refer to a newer post saying that it's not always successful to do it this way because JS files are sometimes not guaranteed loaded completely before calling a JS function inside those external JS files.

Reference links
http://stackoverflow.com/questions/5331921/how-can-i-make-sure-that-some-js-files-are-loaded-before-the-page-continue
http://stackoverflow.com/questions/1795438/load-and-execution-sequence-of-a-web-page

2013年7月25日 星期四

What if BitmapFactory.decodeStream() returns null

If you find an exception error like:

BitmapFactory.decodeStream return null

It could be caused by the following issues:
1)  The image is null, or not exist
2) Surf through the internet and find some people mentioning using BufferedHttpEntity() before calling getContent() is a better way to resolve "sometimes null return" issue

The following shows the code difference between adding BufferedHttpEntity or not:
             InputStream result = null;

            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            result = httpEntity.getContent();


------------------------------------------------------------------------
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(httpEntity);
            result = bufHttpEntity.getContent();


Reference links
http://stackoverflow.com/questions/5832229/problem-loading-some-dynamic-png-image-bitmapfactory-decodestream-retuns-null
https://groups.google.com/forum/#!topic/android-developers/EKOCEVjxe-E
https://groups.google.com/forum/#!topic/android-developers/FxuL81277ZY

2013年7月24日 星期三

new JS<->Android interface code

...
->Compose received information fragments into one javascript code
js_url = intent.getStringArrayExtra("js_url");
playfunc = intent.getStringExtra("playfunc");

for (int i = 0;i < js_url.length;i++) {
            String funcPtr = "";
            if (js_url[i] == null) {
                break;
            }
            if (i == 0) {
                funcPtr = playfunc;
            }
            js_url_string = js_url_string +
                    " var script=document.createElement('script'); "
                    + " script.setAttribute('type','text/javascript'); "
                    + " script.setAttribute('src', '"
                    + js_url[i]
                    + "'); "
                    + " script.onload = function() {"
                    + funcPtr + ";"
                    + "};"
                    + " document.getElementsByTagName('head')[0].appendChild(script); ";
        }
       
        System.out.println(js_url_string);
->Create a WebView not used for display but executing javascript code       
        WebView myWebView = new WebView(this);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        System.out.println(android.os.Build.VERSION.SDK_INT);
        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
        }
        myWebView.setWebChromeClient(new WebChromeClient());
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + js_url_string
                        + "})()");
            }
        });
->Create a JS-Android Interface name
        myWebView.addJavascriptInterface(new MyAndroidJSInterface(this, SecondActivity.class), "atv");
->As a activation key for starting off running javascript later
        myWebView.loadUrl("file:///android_asset/tmp.html");

Create Document elements using javascript

js_url_string = js_url_string +
                    " var script=document.createElement('script'); "
                    + " script.setAttribute('type','text/javascript'); "
                    + " script.setAttribute('src', '"
                    + js_url[i]
                    + "'); "
                    + " script.onload = function() {"
                    + funcPtr + ";"
                    + "};"
                    + " document.getElementsByTagName('head')[0].appendChild(script); ";


myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + js_url_string
                        + "})()");
            }
        });


Reference links
http://stackoverflow.com/questions/8227612/how-to-create-document-objects-with-javascript
http://www.javascriptkit.com/javatutors/dom2.shtml

use GridView to display pictures being downloaded through HTTP

[layout]

<GridView
    android:id="@+id/grid_view_2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:numColumns="auto_fit"
    android:columnWidth="90dp"
    android:horizontalSpacing="10dp"
    android:verticalSpacing="10dp"
    android:gravity="center"
    android:stretchMode="columnWidth" >
</GridView>


[adapter]

public class MyImageAdapter extends BaseAdapter {
    private Context mContext;
    private int total_pic_cnt = 0;
    public Bitmap image[];
    public String select_func[];
    public String js_url[];

    // Constructor
    public MyImageAdapter(Context c, String jsFileURL[], Bitmap pic[], String func[], int num){
        mContext = c;
        js_url = jsFileURL;
        image = pic;
        select_func = func;
        total_pic_cnt = num;
    }
   
    @Override
    public int getCount() {
        return total_pic_cnt;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView = new ImageView(mContext);
        imageView.setImageBitmap(image[position]);
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        imageView.setLayoutParams(new GridView.LayoutParams(100, 70));
        return imageView;
    }
}


[new a adapter and start off the rendering]

MainActivity UIthread = (MainActivity)mContext;
UIthread.adapter = new MyImageAdapter(UIthread, jsFileURL, pic, onSelect_func, loaded_pic_cnt);
UIthread.rendering();

[download and convert an image]

pic[loaded_pic_cnt] = BitmapFactory.decodeStream(parser.getURLAsInputStream(n2_1_image));

[draw]

public void rendering() {
        runOnUiThread(new Runnable() {
            public void run() {
                    System.out.println("render it!");
                    selectMenu = (GridView)findViewById(R.id.grid_view_2);
                    selectMenu.setAdapter(adapter);
                    selectMenu.setOnItemClickListener(new OnItemClickListener() {
                        @Override
                        public void onItemClick(AdapterView<?> parent, View v,
                                int position, long id) {
  ...
                        }
                    });
            }
         });
    }



Reference links

use VideoView to play HTTP streaming

[code snippet]
public void playVideo(final String video_url) {
        runOnUiThread(new Runnable() {
            public void run() {
                VideoView mVideoView = (VideoView) findViewById(R.id.VideoView);
                mVideoView.setVisibility(View.VISIBLE);  
                mVideoView.setMediaController(new MediaController(mcontext));
                mVideoView.setVideoURI(Uri.parse(video_url));
                mVideoView.requestFocus();
                mVideoView.start();
            }
        });
    }


[layout]
<VideoView
         android:layout_height="fill_parent"
         android:layout_width="wrap_content"
         android:id="@+id/VideoView" />


Reference links
http://android-coding.blogspot.tw/2011/03/simple-example-using-videoview-to-play.html

http://stackoverflow.com/questions/14354933/error-in-opening-streaming-video

2013年7月22日 星期一

Check and Set DNS server using nslookup

#nslookup

Check the DNS the following URL uses:
>trailers.apple.com

Set DNS server:
>server <DNS IP>

Check again:
>trailers.apple.com

Convert InputStream containing img data into Bitmap

Bitmap pic = BitmapFactory.decodeStream(InputStream is);

HTTP retrieved content into String/InputStream

public String getURL(String url) {
        String result = null;

        try {
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            result = EntityUtils.toString(httpEntity);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
   
    public InputStream getURLAsInputStream(String url) {
        InputStream result = null;

        try {
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            result = httpEntity.getContent();

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

2013年7月21日 星期日

"Any-Class" declaration

Class<?> mClass;

Then Cast mClass to any Class you want.

Send/Receive an intent

Sending side:

Intent i = new Intent(UIthread, SecondActivity.class);
i.putExtra("js_url", adapter.js_url);
i.putExtra("playfunc", adapter.select_func[position]);
startActivity(i);

Receiving side:

Intent intent = getIntent();
js_url = intent.getStringArrayExtra("js_url");
playfunc = intent.getStringExtra("playfunc");

Render UI out side of UI thread

Rendering is not available for threads other than the main thread, or UI thread. Put the following run thread creation method in a background function within an activity achieves the goal:

/* 3) Run a UI thread to catch the UI update*/
            runOnUiThread(new Runnable() {
                public void run() {
                    if (rendernow == 1) {
                        selectMenu = (GridView)findViewById(R.id.grid_view);
                        selectMenu.setAdapter(adapter);
                        selectMenu.setOnItemClickListener(new OnItemClickListener() {
                            @Override
                            public void onItemClick(AdapterView<?> parent, View v,
                                    int position, long id) {

                                    /* Handling click event for GridView */
                            }
                        });
                        rendernow = 0;
                    }
                }
             });


Reference links
http://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi

2013年7月16日 星期二

Change MAC address in Windows

1) go to "Local Area Network Properties"
2) click "configure"
3) Choose "Advanced" tab and select "Locally administered address"
4) Input target MAC address into "Value" field
5) done

Reference links
http://www.online-tech-tips.com/computer-tips/how-to-change-mac-address/

Resolve cross-domain error

The scheme is that I call a JS function after loading a JS file from web, and in the JS function it in the end sends out a HTTP GET request to obtain a web page which I use for further tasks. I run into a problem when the HTTP GET request is sent and the error message shows:

XmlHttpRequest error: Origin null is not allowed by Access-Control-Allow-Origin

This is a common CORS issue, which is short for Cross-Origin Resource Sharing. I surfed the internet a lot and found some possible answers to this question. One of them is to get the server side to set up a "Access-Control-Allow-Origin: *" text in the header to tell clients that it allows CORS. However, in my case I have not got to the point of completing sending out a request, but rather got rejected before it. This could be a local check for origin, so I checked the development guide for possible web settings and found this one:

void setAllowUniversalAccessFromFileURLs(boolean flag)
Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.

And it works ! The following is part of my code which points out my use of it:

notice: The setting is true by default under API level 16 and false by default equivalent or above API level 16. The API is only defined after API level 16.

[JavaScriptHandler activity]
in pptv.js it uses the following Android interface, so I create them in this activity to save the result:
atv.loadXML(atv.parseXML(doc));

public class JavaScriptHandler {
    Webview_test parentActivity;
   
    public JavaScriptHandler(Webview_test activity) {
        parentActivity = activity;
    }

    @JavascriptInterface
    public String parseXML(String val){
        System.out.println("parseXML...");
        return val;
    }
   
    @JavascriptInterface
    public void loadXML(String val){
        System.out.println("loadXML...");
        this.parentActivity.javascriptCallFinished(val);
    }
}


[WebView activity]
public class Webview_test extends Activity {
    WebView myWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_test);
        // Show the Up button in the action bar.
        setupActionBar();
        Intent intent = getIntent();
        String url = intent.getStringExtra("xml_url");
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);

        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
           
myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);

        }
        myWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                        System.out.println("got here!");
                        return super.onJsAlert(view, url, message, result);
               }
        });  
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.bgColor='#FF0000';" //turns to red the background color
                        + " var script=document.createElement('script'); "
                        + " script.setAttribute('type','text/javascript'); "
                        + " script.setAttribute('src', 'http://trailers.apple.com/js/pptv.js'); "
                        + " script.onload = function() {"
                        + "       pptvLoad();"
                        + "};"
                        + " document.getElementsByTagName('head')[0].appendChild(script); "
                        + "})()");
            }
        });
        myWebView.addJavascriptInterface(new JavaScriptHandler(this), "atv");
    }
...
    public void javascriptCallFinished(String val){
         System.out.println(val);
    }
...   
    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("file:///android_asset/testalert2.html");
    }
}


Reference links
http://stackoverflow.com/questions/14825943/setallowuniversalaccessfromfileurls
cross-site HTTP request (CORS)
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control
Introduction to XMLHttpRequest class in Javascript
http://ajaxpatterns.org/XMLHttpRequest_Call
development guide
http://developer.android.com/reference/android/webkit/WebSettings.html

2013年7月15日 星期一

Retrieve JS function result in Android application

Thanks to a blogger at the reference link for a awesome explanation !

The details can be referenced at the link at the bottom. The following is a recording of what I've tested in my workspace:

[JavascriptHandler.java]
import android.webkit.JavascriptInterface;

public class JavaScriptHandler {
    Webview_test parentActivity;
   
    public JavaScriptHandler(Webview_test activity) {
        parentActivity = activity;
    }

    @JavascriptInterface
<-- need to have this since API lvl 17 for any methods that could be used from JS function
    public void setResult(int val){
        this.parentActivity.javascriptCallFinished(val);
    }
}


[webview_test.java]
public class Webview_test extends Activity {
    WebView myWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_test);
        // Show the Up button in the action bar.
        setupActionBar();
        Intent intent = getIntent();
        String url = intent.getStringExtra("xml_url");
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        myWebView.setWebChromeClient(new WebChromeClient());   
        myWebView.setWebViewClient(new WebViewClient());
        myWebView.loadUrl("file:///android_asset/testRet.html");
        myWebView.addJavascriptInterface(new JavaScriptHandler(this), "MyHandler");
    }

    public void javascriptCallFinished(int val){
         System.out.println(val);
    }  
    

...

    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("javascript:window.MyHandler.setResult(ret_something());");
    }


[testRet.html]
<HTML>
<center><b><u>JavaScript</u></b></center>
<script language="JavaScript">
function ret_something() {
return 999;
}
</script>
</head>
<body>
<p> Enter your name here <input type="text" id="namefromjs" />
<p> <input type="button" onclick= "setData()" value="Call Android sayHelloFromAndroid() method from JS">
<p> Data pass from Android</p>
<p> <input type="button" onclick= "getData()" value="Get Name From Android EditText">
<p> Name from Android is <input type="text" id="namefromAndroid" />
</body>
</HTML>


------------------------------------------------------------------------------------------------------
Sometimes you want to call JS functions after loading a web page. Or, you just simply feel like using some JS funtions to do something. The following code snippet shows a way to achieve this:

[Webview_test activity]
public class Webview_test extends Activity {
...
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.bgColor='#FF0000';" //turns to red the background color
                        + " var script=document.createElement('script'); "
                        + " script.setAttribute('type','text/javascript'); "
                        + " script.setAttribute('src', 'file:///android_asset/pptv.js'); "
                        + " script.onload = function(){ "
                        + "     window.MyHandler.setResult(test()); "
                        + " }; "
                        + " document.getElementsByTagName('head')[0].appendChild(script); "
                        + "})()");
            }

        });
 ...
    }

...
    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("file:///android_asset/testalert2.html");
    }

}

[pptv.js]
function test()
{
    return "test...";
}


[testalert2.html]
<HTML>
<head>
<center><b><u>JavaScript</u></b></center>
</head>
<body>
<p> Enter your name here <input type="text" id="namefromjs" />
<p> <input type="button" onclick= "setData()" value="Call Android sayHelloFromAndroid() method from JS">
<p> Data pass from Android</p>
<p> <input type="button" onclick= "getData()" value="Get Name From Android EditText">
<p> Name from Android is <input type="text" id="namefromAndroid" />
</body>
</HTML>


Reference links
http://blog.objectgraph.com/index.php/2012/03/16/android-development-javascript-bridge-example-fully-explained/
http://stackoverflow.com/questions/5649111/android-webview-loading-javascript-file-in-assets-folder

2013年7月14日 星期日

Call a Javascript function from Android application in a WebView

[Layout]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_send"
        android:onClick="testAndroid2JS" />
<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>
</LinearLayout>


[Android2JStest Activity]
public class Android2JStest extends Activity {
    WebView myWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_Android2JStest);
        // Show the Up button in the action bar.
        setupActionBar();
 ...
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        myWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                        System.out.println("got here!");
                        return super.onJsAlert(view, url, message, result);
               }
        });   
        myWebView.setWebViewClient(new WebViewClient());
    }


...
<type 1>
The target JS function is embedded in the html instead of in a include JS src.

    public void testAndroid2JS(View view) throws IOException
    {
        String xml_content = assetFile2Str("testalert2.html");
       
        myWebView.loadDataWithBaseURL("file:///android_asset/", xml_content, "text/html", "utf-8", null);
        myWebView.loadUrl("javascript:testalert()");    

    }

loadUrl() is a asynchronous method and will dispatch from current thread for execution. Therefore, if you'd like to use loadUrl() instead of loadDataWithBaseURL(), either you call it in onCreate(), or you need to make sure the page has been fully loaded before using any JS function in it.

<type 2>
Directly read content from a JS file and modify the content to call the target JS function.
    public void testAndroid2JS(View view) throws IOException
    {
        String xml_content = assetFile2Str("testalert.js");
        myWebView.loadUrl("javascript:"+ xml_content + "testalert();" + "");
    }

}

Reference links
http://stackoverflow.com/questions/4761696/android-inject-in-webview-external-js-execute-function-and-raise-an-alert
http://stackoverflow.com/questions/5271898/javascript-alert-not-working-in-android-webview
http://stackoverflow.com/questions/3971832/android-loading-an-external-js-file-into-a-webview-then-accessing-its-functi
http://forums.xamarin.com/discussion/3467/loading-dynamic-html-and-javascript-from-assets-in-a-webview

2013年7月11日 星期四

Read a asset file into a String

private String assetFile2Str(String path) throws IOException {
InputStream is = getAssets().open(path);
int size = is.available();

byte[] buffer = new byte[size];
is.read(buffer);
is.close();

String str = new String(buffer);
return str;
}

where path should be a relative path to the base URL, for example, if there is a target file located at:

file:///android_asset/www/test.html

then you should put "www/test.html" as path.

Reference links
http://stackoverflow.com/questions/11157876/read-html-file-from-assets

2013年7月10日 星期三

Lua TOI

I've worked on a project with Cisco in Silicon Valley for a AP switch product. The project was lasting for 1 ~ 2 years, so I spent quite a while in the US and that really is a great experience no matter on the perspective of working or living there. OK, my tasks there were like many :
1) Figuring out how to run IOS, Cisco's close OS, on a core, while running Linux on the other. It's a Broadcom 963268 solution which is a dual core chip. It involves in very underlying code change like relocating a 2nd CFE which then can run Linux on the 2nd core, and exception-vector split between IOS and Linux, and interrupt-related register control and OS code change, etc.
2) Inter-core communication for two cores to be able to talk to each other by using software interrupts bits as notification, and share memory as a data-change pool.
3) Newly implemented WIFI CLI in Linux which mimics most of what they've already had in IOS for WIFI-related commands. I use Lua, a very handy scripting language, to compose these commands as a user friendly interface which also meets IOS's strict testing criteria.

I summarize the Lua use in a presentation for future reference.

Run APK on Android devices from Eclipse on PC

1) If want to run on a connected Android device, make sure you have installed the driver successfully
2) Eclipse->Run->Run configuration->Run
3) Select a device where you'd like to run your APK (either an emulator or a real device connected to PC)

WebView

WebView class provides you ways to embed a web application or a web page as part of a client application. Binding JavaScript code and Android code is also doable through WebView, which provides a way for host application to control some of the features in client application. The following is the typical layout and use of WebView:

<?xml version="1.0" encoding="utf-8"?>
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>
--- 
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl(url); 
 
JavaScript is disabled by default for WebView. We can enable it by doing the following:
 
 WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true); 
 
When you're designing your own web application, you can create interfaces between your JavaScript 
code and client-side Android code. The idea is that you create a class as well as a public method in 
your Android application which then can be used by JavaScript code in web application. 
Find the reference links for more details.

Reference links
http://developer.android.com/guide/webapps/webview.html 

2013年7月8日 星期一

Get INTERNET permission

Add the following line before and outside of the application tag in manifest:

<uses-permission android:name="android.permission.INTERNET" />

Use Document class to parse XML/HTML content

Recently I got an assignment which asks me to do the XML parsing on a Android device. It's part of a project which requires parsing redirected XML file and obtaining embedded streaming information and then stream them and being able to watch them on a Android device. So I start it off by searching for possible solution to XML parsing in Android and luckily, there are a couple of ways to achieve that goal. One way I found is to use XmlPullParser class, which I'll leave it to another post to discuss in the future because I'm not going to use it in this project, the other is what I'm going to discuss in this post, Document class, and I found it very easy and flexible to use compared to XmlPullParser class.

I've not dug through all the details of Document class so there will be no comprehensive illustration of this class here. You could refer to the reference link at the end of this post for some details. Document class can be used to parse XML/HTML content and establish a tree for users to search for desirable information. In order to get what I need from a XML file, the following is a code snippet I copied and modifed from one of the reference link I list at the end of this post:

public class XMLParser {

    public XMLParser() {

    }
   
    public String getXmlFromUrl(String url) {
        String xml = null;

        try {
           
//To obtain target XML file with a known URL thourgh HTTP client
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            xml = EntityUtils.toString(httpEntity);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return xml;
    }
    

   //Transform XML string to Document structure
    public Document getDomElement(String xml){
        Document doc = null;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {

            DocumentBuilder db = dbf.newDocumentBuilder();

            InputSource is = new InputSource();
                is.setCharacterStream(new StringReader(xml));
                doc = db.parse(is);

            } catch (ParserConfigurationException e) {
                Log.e("Error: ", e.getMessage());
                return null;
            } catch (SAXException e) {
                Log.e("Error: ", e.getMessage());
                return null;
            } catch (IOException e) {
                Log.e("Error: ", e.getMessage());
                return null;
            }
                // return DOM
            return doc;
    }
    

   //Search and get a list composed of nodes with the same tag name as str, and pass in the first node
    public String getValue(Element item, String str) {   
        NodeList n = item.getElementsByTagName(str);     
        return this.getElementValue(n.item(0));
    }
     

    //return text value if exist
    public final String getElementValue( Node elem ) {
             Node child;
             if( elem != null){
                 if (elem.hasChildNodes()){
                     for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
                         if( child.getNodeType() == Node.TEXT_NODE  ){
                             return child.getNodeValue();
                         }
                     }
                 }
             }
             return "";
    }
}


The following code snippet shows how this class can be used:

private String xml = null;
private String url = null;
private Document doc = null;

XMLParser parser = new XMLParser();
url = "http://...";
xml = parser.getXmlFromUrl(url);
doc = parser.getDomElement(xml);
displayIndexXml();

private void displayNavXml() {
        NodeList nl = doc.getElementsByTagName("navigationItem");
   
        for (int i = 0; i < nl.getLength(); i++) {
            Element e = (Element) nl.item(i);
            String title = parser.getValue(e, "title");
            String url = parser.getValue(e, "url");
            System.out.println("<"+title+","+url+">");
        }
 }


Target XML in this example:

 <?xml version="1.0" encoding="UTF-8"?><atv>
    <head>
        <script src="http://trailers.apple.com/appletv/us/js/main.js"/>
    </head>
    <body>
        <viewWithNavigationBar id="com.trailers.navigation-bar">
            <navigation currentIndex="0">
                <navigationItem id="nav1">
                    <title>Top Trailers</title>
                    <url>http://trailers.apple.com/appletv/us/index.xml</url>
                </navigationItem>
                <navigationItem id="nav2">
                    <title>Calendar</title>
                    <url>http://trailers.apple.com/appletv/us/calendar.xml</url>
                </navigationItem>
                <navigationItem id="nav3">
                    <title>Browse</title>
                    <url>http://trailers.apple.com/appletv/us/browse.xml</url>
                </navigationItem>
                <navigationItem id="nav4">
                    <title>Search</title>
                    <url>http://trailers.apple.com/appletv/us/searchtrailers.xml</url>
                </navigationItem>
            </navigation>
        </viewWithNavigationBar>
    </body>
</atv>


logcat output:

<Top Trailers,http://trailers.apple.com/appletv/us/index.xml>
<Calendar,http://trailers.apple.com/appletv/us/calendar.xml>
<Browse,http://trailers.apple.com/appletv/us/browse.xml>
<Search,http://trailers.apple.com/appletv/us/searchtrailers.xml>

Reference
http://developer.android.com/reference/org/w3c/dom/Document.html
http://www.androidhive.info/2011/11/android-xml-parsing-tutorial/

logcat does not work

Sometimes you'll find logcat does not show anything at all in your eclipse. Some people in a forum mention about checking out selected emulator in the DDMS, but that does not work for me. One person says that it's like a temporary eclipse out-of-function on logcat so just restart your eclipse then everything will get back to normal, which does not work for me either. Then I found someone saying press F11 which does thing like restarting a new emulator and run the project in debug mode. I tried it, and it worked for me ! Thank you stranger !

android.os.NetworkOnMainThreadException

Since Android API level 11, android.os.NetworkOnMainThreadException is added to throw exception when any network operation is performed in UI thread (main thread). The purpose is obviously to avoid possible system hang or display delay due to time-consuming network operations. Instead, one should perform network operations using AsyncTask to create a thread as following:

public class XMLParseTestActivity extends Activity {
    XMLParser parser = new XMLParser();
    String xml = null;
    String url = null;
    Document doc = null;

   
...


    class MyAsyncTask extends AsyncTask<Void, Void, Void>    {
       
        MyAsyncTask()    {
                    
        }
        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            doc = parser.getDomElement(xml);
            displayNavXml();
        }
        @Override
        protected void onPreExecute() {
            String nav_url = "http://trailers.apple.com/appletv/us/nav.xml";
            super.onPreExecute();
            url = nav_url;
        }
        @Override
        protected Void doInBackground(Void... arg0) {
            xml = parser.getXmlFromUrl(url);
            return null;
        }
    }  

   
    public void parseTargetXML(View view) throws IOException {
        MyAsyncTask myWebFetch = new MyAsyncTask();
        myWebFetch.execute();
    }

   
    private void displayNavXml() {
        NodeList nl = doc.getElementsByTagName("navigationItem");
   
        for (int i = 0; i < nl.getLength(); i++) {
            Element e = (Element) nl.item(i);
            String title = parser.getValue(e, "title");
            String url = parser.getValue(e, "url");
            System.out.println("<"+title+","+url+">");
        }
    }


...
}


This code snippet performs a XML parsing operation. It firstly fetches XML through HTTP over internet and then parse the content and display. parseTargetXML() is a onClick() function called whenever a defined button is pressed.

Reference
http://subinsebastien.tumblr.com/post/10319834334/simple-asynctask-example-in-android