顯示具有 programming 標籤的文章。 顯示所有文章
顯示具有 programming 標籤的文章。 顯示所有文章

2013/05/02

如何在 2.3.x android OS 呼叫 java 函式 ?

之前提到 WebView 與 javascript 使用,
但卻不適用於 android 2.3.x, 因為會有問題. (App 直接關閉)

下面是我所使用的方式, 讓我們可以在 2.3.x 去調用 java 函式.

稍微修改了一下所使用的 html, 修改 showMsg 所使用的方式
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Example</title>
<meta name="viewport" content="user-scalable=yes,target-densityDpi=device-dpi,width=device-width" />
<script type='text/javascript'>
function showMsg() {
prompt("This is prompt");
}
</script>
</head>
<body>
<input type='button' value='Click Me' onclick='showMsg();' />
</body>
</html>

執行結果如下圖:


透過這個我們可以利用這個方式來非直接的調用 java 函式,
特別注意的是第 10 行, 如果不 cancel, 畫面就會因為這個 prompt 而無法操作喔.
 WebChromeClient mWebChromeClient = new WebChromeClient() {

  @Override
  public boolean onJsPrompt(WebView view, String url, String message,
    String defaultValue, JsPromptResult result) {

   if ((defaultValue != null) && (defaultValue.length() != 0)) {
    if (defaultValue.equalsIgnoreCase("jstesting")) {
     Toast.makeText(MainActivity.this, R.string.msg_js_call_toast, Toast.LENGTH_SHORT).show();
     result.cancel();
     return true;
    }
   }
   
   return super.onJsPrompt(view, url, message, defaultValue, result);
  }
  
 };

再次修改一下 html 檔.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Example</title>
<meta name="viewport" content="user-scalable=yes,target-densityDpi=device-dpi,width=device-width" />
<script type='text/javascript'>
function showMsg() {
prompt("This is prompt", "jstesting");
}
</script>
</head>
<body>
<input type='button' value='Click Me' onclick='showMsg();' />
</body>
</html>

這樣就可以調用 java 函式了.

如果覺得上面方式有點怪異或麻煩, 也可以使用參考資料 [1] 的方式.
不過對我而言以上方式就是最簡單的方式了.

範例原始碼: WebViewExample4_1

參考資料:
[1]. http://fred-zone.blogspot.tw/2011/12/android-23x-javascript-interface.html

WebView 與 javascript 使用


之前講過怎麼簡單的使用 webview,
這邊也稍微紀錄一下如何在 webview 與 javascript 的使用.

預設 webview 是不能使用 javascript 的, 所以在初始時必須開啟 javascript,
除此之外, 還必須設定 web chrome client, 否則在我的範例則會無法達到效果.
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.getSettings().setJavaScriptEnabled(true);

我所使用的 html 檔案, 點擊按鈕會跳出一個訊息視窗
<!DOCTYPE html>
<html>
    <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <title>Example</title>
        <meta name="viewport" content="user-scalable=yes,target-densityDpi=device-dpi,width=device-width" />
  <script type='text/javascript'> 
   function showMsg() {
    alert('This is alert dialog');
   }
  </script>
    </head>
 <body>
  <input type='button' value='Click Me' onclick='showMsg();'/>
 </body>
</html>

效果如下:


在 Webview 內使用 javascript 就是這麼簡單.

OK ! 接著如果要從 android 呼叫 html 裡的 javascript 呢?
只要使用以下方式就可以呼叫 html 裡的 javscript.
mWebView.loadUrl("javasript:showMsg();");
效果跟上圖一樣, 也是會跳出一個訊息視窗.

反過來, 可以從 html 呼叫 android 的 java 函式嗎 ?
可以, 一樣是透過 javascript 的方式!

2013/04/11

WebView with jsoup html parser

Jsoup 真的非常好用, 尤其搭配 WebView 使用.
以下範例為使用 Jsoup 並且將解悶有漫畫這網站顯示成我所想要的樣子.

Layout
<RelativeLayout 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" >

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:indeterminate="true"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_alignParentBottom="true" 
        android:visibility="gone"/>

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/progress" />

</RelativeLayout>

要將網頁顯示成我們所要的格式, 最要下苦工的就是查看網頁的 html 原始檔, 幸好現在大部分網站的 html 都很有規格,
所以要解析也變得相當容易.
如果透過 WebView 載入畫面, 那麼資料將無法處理, 所以改成透過 HttpURLConnection 來連結網站, 並且處理資料.

 class LoadTask extends AsyncTask<String, Void, String> {

@Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();

String url = params[0];

try {

Document doc = HttpConnection
.connect(url)
.followRedirects(true)
.get();
if (doc == null) {
return null;
}

Element el = doc.select("ul#framemain_1").first();
if (el == null) {
return null;
}

Elements lis = el.select("li");

/** parse html data and create our own html file **/
sb.append("<html>");
sb.append("<title>WebViewExample Test</title>");
sb.append("<head></head>");
sb.append("<body style='margin: 0px; padding: 0px;'>");
sb.append("<table width='100%' border='1' cellspacing='0' cellpadding='5'><tbody>");
for (Element li: lis) {
Element cover = li.select("a > img").first();
if (cover != null) {
cover.parent().remove();
cover.attr("width", "100px");
cover.attr("height", "136px");
}
sb.append("<tr>");
if (cover == null) {
sb.append("<td colspan='2'>"+li.html()+"</td>");
} else {
sb.append("<td width='100px' height='136px'>"+cover.outerHtml()+"</td>");
sb.append("<td>"+li.html()+"</td>");
}
sb.append("</tr>");
}
sb.append("</body></html>");
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}

@Override
protected void onPostExecute(String result) {
if (result == null) {
Toast.makeText(MainActivity.this, R.string.msg_fail, Toast.LENGTH_SHORT).show();
} else {
mWebView.loadDataWithBaseURL(null, result, "text/html", "UTF-8", null);
}
}
}

主要工作就這麼簡單, 讀取 html, parse 出想要的, 然後寫一份所想要顯示的 html. 完成 !
結果如下:




原始碼: WebViewExample_3

參考 :
[1] Jsoup
[2] 怎麼使用 android 的 webview
[3] WebView with Progressbar

WebView with Progressbar

在 WebView 加上 progressbar 讓使用者知道目前載入進度,

主要利用 WebViewClient 和 WebChromeClient

WebViewClient 可以知道載入開始和載入結束, 可用來判斷是否該顯示 ProgressBar 或者該隱藏
WebChromeClient 可用來知道載入進度

Layout
<RelativeLayout 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" >

<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:indeterminate="true"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_alignParentBottom="true" 
android:visibility="gone"/>

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/progress" />

</RelativeLayout>

主程式碼
package tw.clotai.webviewexample;

import tw.clotai.webviewexample.R;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

public class MainActivity extends Activity {

WebView mWebView = null;
ProgressBar mBar = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mWebView = (WebView)findViewById(R.id.webview);
mBar = (ProgressBar)findViewById(R.id.progress);

mWebView.setWebViewClient(mWebViewClient);
mWebView.setWebChromeClient(mWebChromeClient);
mWebView.loadUrl("http://tw.yahoo.com");
//mWebView.loadUrl("file:///android_asset/example.html");
}

WebViewClient mWebViewClient = new WebViewClient() {

@Override
public void onPageFinished(WebView view, String url) {
mBar.setVisibility(View.GONE);
}

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
mBar.setVisibility(View.VISIBLE);
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
};

WebChromeClient mWebChromeClient = new WebChromeClient() {

@Override
public void onProgressChanged(WebView view, int newProgress) {
mBar.setIndeterminate(false);
mBar.setProgress(newProgress);
}

@Override
public void onReceivedTitle(WebView view, String title) {
if ((title != null) && (title.trim().length() != 0)) {
setTitle(title);
}
}
};
}




原始碼: WebViewExample_2

參考:
[1] 基本使用 webview

怎麼使用 android 的 webview

怎麼使用 android 的 webview, 很簡單, 不過這邊我一樣紀錄一下如何使用.


模擬器: android os 版本 2.3.3
SDK: android-17

以下是我的 Layout 檔案.
且記得在 AndroidManifest.xml 裡面加上網路權限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<RelativeLayout 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" >

    <WebView 
        android:id="@+id/webview"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_alignParentTop="true" />

</RelativeLayout>


我的 MainActivity 裡面內容如下:
public class MainActivity extends Activity {

 WebView mWebView = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mWebView = (WebView)findViewById(R.id.webview);
  
  mWebView.loadUrl("http://tw.yahoo.com");
 }
}

這樣基本上就會載入畫面了, 可是如果實際測試, 會發現會使用外部的瀏覽器開啟.
原因是因為當載入時, yahoo 判斷是 Mobile 所以轉址, 而這轉址的動作我們並沒有處理, 所以就會開啟預設的瀏覽器瀏覽.
解決方式如下 :
public class MainActivity extends Activity {

 WebView mWebView = null;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mWebView = (WebView)findViewById(R.id.webview);
  
  mWebView.setWebViewClient(mWebViewClient);
  mWebView.loadUrl("http://tw.yahoo.com");
 }
 
 
 WebViewClient mWebViewClient = new WebViewClient() {
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
   view.loadUrl(url);
   return true;
  }
 };
}
顯示結果如下 :



2013/04/05

Gallery3 怎麼利用 REST API 得到 API Key

怎麼利用 REST API 得到 api key, 基本上得到 api key 是為了之後的動作不需要再次使用使用者名稱和密碼, 為了安全性.
要可以使用 REST API, Gallery3 必須開啟 REST Module.
以下使用 curl 當範例來使用 REST API.

假設我架設一個 gallery3 網站, 在
網址: http://www.test.com/gallery3/index.php
使用者: test
密碼: test
下面是使用方式
curl -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.91 Safari/537.11" -H "X-Gallery-Request-Method: post" --data-urlencode "user=test" --data-urlencode "password=test" "http://www.test.com/gallery3/index.php/rest"


這樣會得到你的 api key, 後面就可以繼續使用這 api key 作其他的 request 了.

2013/03/30

非 ascii code 網址問題

今天收信有人回報 xgallery 有問題, 無法使用.
檢查並且測試一下對方所給的網址, 發現原來是網址的問題

一般正常的網址大概如下, 用正常 HttpURLConnection 一切都沒問題.
http://www.yahoo.com

但是如果是以下的網址呢 ?
http://www.yahoo.com/abcabc재/test/123.html
用一般正常的瀏覽器看是沒有問題, 但如果使用 HttpURLConnection 就會出現找不到網址的現象.
原因很簡單, 就是要把網址 encode.

原本很簡單的我就直接使用 URLEncode("http://www.yahoo.com/abcabc재/test/123.html", "UTF-8"),
這樣不行, 這樣會連 ":", "/" 等都給 encode 起來, 所以只能一個一個去 encode.

解決方式如下:

public static String getEncodeURL(String url) {
 StringBuilder sb = new StringBuilder();
 Uri uri = Uri.parse(url);
 
 String path = uri.getPath();
 List paths = uri.getPathSegments();
 
 int index = url.indexOf(path);
 
 sb.append(url.substring(0, index));
 for (String s: paths) {
  try {
   sb.append("/"+URLEncoder.encode(s, "UTF-8"));
  } catch (UnsupportedEncodingException e) {
   sb.append("/"+s);
  }
 }
 return sb.toString();
}

只要把每個網址用這個 method 包起來就可以了

PS: 上面那個 </string> 請忽略, 不知道為啥會出現

2013/03/08

Android 全螢幕切換

Android 全螢幕切換, 可透掛以下方式達成.
private void toggleFullscreen() {
 WindowManager.LayoutParams attrs = getWindow().getAttributes();
 
 if ((attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
  attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
 } else {
  attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
 }
 getWindow().setAttributes(attrs); 
}

使用 HttpURLConnection Post

使用 HttpURLConnection 做 Post 基本上很簡單, 可以透過以下方式達成, 以下都沒有做 Exception 或者 Error 處理.
StringBuilder uriparameters = new StringBuilder();
uriparameters.append("user=" + URLEncoder.encode(user, "UTF-8"));
uriparameters.append("&password="+ URLEncoder.encode(pass, "UTF-8"));
int parameterLen = uriparameters.length();
/** sURL 是想要 post 的網址 **/
URL url = new URL(sURL);
HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection();

/** 假裝成瀏覽器 **/
conn.setRequestProperty(
  "User-Agent",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.91 Safari/537.11");
conn.setConnectTimeout(15000);
conn.setReadTimeout(15000);
conn.setRequestMethod("POST");

conn.setRequestProperty("X-Gallery-Request-Method", "post");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", Integer.toString(paramsLen));

conn.setDoOutput(true);
conn.setDoInput(true);

DataOutputStream wr = null;
wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes(uriparameters.toString());
wr.flush();
wr.close();
wr = null;
/** 取得 post 後所得到的 response **/
InputStream in = conn.getInputStream();
但是萬一是要上傳檔案呢 ? 這就不是這麼簡單了, 但是一樣可以用類似的方式達成我們的目的. 其實主要就是我們必須要自己去把整個post body 自己寫上去, 如果不知道怎麼寫, 簡單方式就是透過 wireshark 抓取自己透過瀏覽器上傳的封包, 接著再把封包內容寫上去這樣就好了.
final String POST_CRLF    = "\r\n";
final String POST_TWO_HYPHENS  = "--";
final String POST_BOUNDARY   =  "*****";
final String POST_UPLOAD          = POST_TWO_HYPHENS+POST_BOUNDARY+POST_CRLF;
final String POST_UPLOAD_END   = POST_CRLF+POST_TWO_HYPHENS+POST_BOUNDARY+POST_TWO_HYPHENS+POST_CRLF;

URL url = new URL(sURL);
HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection();

conn.setRequestProperty(
  "User-Agent",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.91 Safari/537.11");
conn.setConnectTimeout(15000);
conn.setReadTimeout(15000);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("X-Gallery-Request-Method", "post");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + Const.POST_BOUNDARY);


conn.setDoOutput(true);
conn.setDoInput(true);

DataOutputStream wr = new DataOutputStream(conn.getOutputStream());

wr.writeBytes(Const.POST_UPLOAD);
wr.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"");
wr.write(f.getName().getBytes("UTF-8"));
wr.writeBytes("\"" + Const.POST_CRLF);
wr.writeBytes("Content-Type: application/octet-stream" + Const.POST_CRLF);
wr.writeBytes(Const.POST_CRLF);

BufferedInputStream bin = new BufferedInputStream(fin);
byte[] rdata = new byte[4096];
int count = 0;
/** total upload length **/
int ulen = 0;

while ((count = bin.read(rdata)) != -1) {
 ulen += count;
 wr.write(rdata, 0, count);
}
wr.writeBytes(Const.POST_UPLOAD_END);
wr.flush();
wr.close();
wr = null;

InputStrem in = conn.getInputStream();