2011年10月21日 星期五

[Android] javascript injection in WebView

上次寫了一篇"startActivityForResult and callback in WebView", 本篇則是上次這篇的延伸應用, 這是有人問我如何inject一整個javascript file到一個web page內(剛剛回顧了一下自己這篇, 發現我把它叫做javascript injection)

其實原理是一樣的, 在載入完原本的web page之後, 一樣透過URL來插入script:

mWebView.loadUrl("javascript:var js = document.createElement('script');js.type = 'text/javascript';js.src = 'http://my_host/1.js';document.getElementsByTagName('head')[0].appendChild(js);");

一樣是透過"javascript:"來inject, 不一樣的只是, 這次我要插入的是一整個js檔, 所以這串javascript的目的就是要建立一個新的script element, 並將它插入head裡, 這樣任務就達成了

但這方法的缺點是, 來源必須是一個url, 也就是要把script file放在server才可以, 如果script是來自應用程式本身, 比如說放在應用程式apk裡面, 或是放在data partition就不行了

在Honeycomb之前的版本, 我還沒想到一個比較好的解法, 但Honeycomb (API level 11, 含11)之後就有一個比較簡單的解法了

作法就是overwrite WebViewClient的shouldInterceptRequest,這似乎就是為了類似的用途而生的呀~~

@Override
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
if(url.startsWith("asset://")) {
String fileName = url.substring(8);
try {
WebResourceResponse resp = new WebResourceResponse("text/javascript", "UTF-8", MyActivity.this.getAssets().open(fileName));
return resp;
} catch (IOException e) {
e.printStackTrace();
}
}
return super.shouldInterceptRequest(view, url);
}
view raw gistfile1.java hosted with ❤ by GitHub

這邊我將來自於apk asset目錄裡的檔案的url定義成"asset://", 因此, 想當然耳, 要導向的url就是這種, 作法也很簡單, 將asset的input stream包裝成WebResourceResponse就可以了, 這樣只要"js.src="後面的url是"asset://xxx.js", 這js的來源就是apk裡的asset

缺點是, 這方法只適用API level 11之後

延伸應用? 其實應該可以利用這個API做出一個lightweight版本的client side serlvet (這樣叫好像也不是很貼切, 反正就是不需要透過http去存取), 不過因為資訊只有url可以使用, 因此不能implement "POST"...

 

題外話, 這個我是在Ice cream sandwich的emulator上測試的, 不過真的要小抱怨一下, 開個emulator要開很久, 看篇漫畫結束後還沒跑完, 如果叫developer完全用emulator開發, 真的會抓狂吧....這樣開發者的開發意願也會降低吧.... = ="

[Android] If you can't remove it, at least you can still "disable" it

Android 4.0 Ice Cream Sandwich 有一個新功能是, 使用者可以停用(Disable)系統上預載的應用程式, 以往系統預載的應用程式是不能被刪除的, 現在, 新的版本多了一個按鈕讓你可以停用它:

Device-2011-10-22-001443
當你把預載應用程式的icon拖到App Info就可以看到一個Disable的按鈕, 按下去後, 你就不會在程式啟動介面上看到他了

這是中國人所謂的..."眼不見為淨"嗎?

被Disable掉的應用程式基本上並沒被移除, 它還是在你手機裡面, 並不會因此多出一些可使用空間, 只是你看他不爽, 以後就可以不用再看到他了....(呃, 不爽用就不要用不就好了)

這是新功能嗎? 對這介面上來說...這按鈕...是新的

PackageManager裡面有個叫做setApplicationEnabledSetting , 這用途就是用來作這種事的, 所以做這樣一個功能到底多複雜呢?

PackageManager pm = getPackageManager(); pm.setApplicationEnabledSetting("com.geekyouup.paug.awesomepager", PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);

就差不多上面那樣

當然啦, 是沒辦法隨便寫一個軟體去disable人家的應用程式的, 如果可以, 不就天下大亂了, 這API只能用在跟你的應用程式相同的uid的package

不過對於系統應用程式來說, 就沒這限制了吧

至於這API啥時有的?        Since API level "1"

 

Ok, So.....

 

We got a new feature..........

 

2011年10月20日 星期四

[筆記] Tabs on JQuery Mobile and Sencha touch

JQuery mobile跟Sencha touch都是蠻完整的mobile web framework, 兩者各有擅長, 比較起來以開發的角度我比較喜歡JQuery所標榜的"Write less, Do more"的哲學下的架構, 而不喜歡Sencha touch把一堆html寫到code裡面去, 但Sencha touch又有比較好的UI look and feel

以tab panel為例,

Sencha touch:

Photo_11-10-21_1_33_28
jQuery mobile:
Photo_11-10-21_12_25_52

這兩個作法大異其趣 

Sencha 的HTML 內容

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Tabs 2</title>
<link rel="stylesheet" href="touch/resources/css/sencha-touch.css" type="text/css">
<script type="text/javascript" src="touch/sencha-touch-all.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub

裡面除了script以外根本就是空的, UI的創建放在app.js(以這範例而言)裡如下:

https://gist.github.com/1301828.js?file=gistfile1

Tabs的內容在哪? items裡分別就是兩個tab, html直接以字串的形態寫在裡面, 老實說, 我覺得這很醜, 也容易出問題, 如果頁面的內容是相當複雜的, 這樣並不是很好

 

再看看jQuery Mobile的作法:

<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Tab test</title>
<link rel="stylesheet"
href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script
src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
<body>
<div data-role="page" id="home">
<div id="content" data-role="content">
<h1>I'm Tab1</h1>
<img src="http://images.apple.com/home/images/hero.jpg" />
</div>
<!-- footer -->
<div data-role="footer" data-position="fixed">
<!-- /navbar -->
<div data-role="navbar">
<ul>
<li><a href="#home" data-icon="grid" >Home</a>
</li>
<li><a href="#test" data-icon="info">Test</a>
</li>
</ul>
</div>
<!-- /navbar -->
</div>
<!-- /footer -->
</div>
<div data-role="page" id="test">
<div id="content" data-role="content">
<h1>I'm Tab2</h1>
</div>
<!-- footer -->
<div data-role="footer" data-position="fixed">
<!-- /navbar -->
<div data-role="navbar">
<ul>
<li><a href="#home" data-icon="grid" >Home</a>
</li>
<li><a href="#test" data-icon="info">Test</a>
</li>
</ul>
</div>
<!-- /navbar -->
</div>
<!-- /footer -->
</div>
</body>
view raw gistfile1.html hosted with ❤ by GitHub

這份source code有點偷懶, 剪剪貼貼過來的, 不過其實就這麼一個html, 並不需要寫額外的javascript code, 乾淨多了  

如果也可以用類似的寫法寫Sencha touch的UI似乎應該會比較好一點, 像是這樣寫:

<div data-role="page" id="homepage">
<div id="home" data-title="home" data-role="tab" data-icon="home">
<h1>I'm Tab1</h1>
<img src="http://images.apple.com/home/images/hero.jpg" />
</div>
<div id="test" data-title="test" data-role="tab" data-icon="info">
<h1>I'm Tab2</h1>
</div>
</div>
view raw gistfile1.html hosted with ❤ by GitHub

做了個實驗, 剛寫下這段code把上面那段轉成跟第一個範例一樣的畫面:

https://gist.github.com/1301855.js?file=gistfile1

看來如果再多層包裝其實也不用醜醜的通通把UI hard code到js codes裡面去