2010-10-11 19:31 墳墓 (Brian Hsu)
話說為了下一版的 Maidroid Reminder 要加入的與 Google Calendar 同步的功能,這一陣子在研究Google Data API。
原本的 gdata-java-client 雖然比較完整方便,但我卻是一直無法讓他正常地在 Scala/Android/Proguard 的組合下運作,要我回去用 Java 寫 Android 程式我也不想幹,於是只好試著看看有沒有啥替代方案。
目前看到的是兩個,一個是用 CalDev,也就是 iCal 的格式,函式庫方面可以用 ical4j,也在去年實驗過可以在 Scala/Android/Proguard 的方案上使用,但相較之下,由於 Scala 本身處理 XML 的能力很強,所以還是希望可以找以 Atom 為基礎的解決方案。
於是乎,就試著用比較新,但仍然在實驗中的 google-api-java-client,但很不幸的,文件少到可憐,而雖然有完整的範例程式,但程式碼卻有點大,不太好入門。
對我而言,我最主要的只要能取得行事曆的 XML 格式的 Feed 就好,Parser 的部份我可以直接用 Scala 來幹掉, 所以我試著追了一下範例程式碼,並且將其簡化到只剩最最最基本的使用者認證和取得 Atom Feed 的部份,成果如下,而這段程式也確定可以在 Scala/Android/Proguard 的組合下運作。
簡單來說,要做的就是以下幾個步驟:
- 建立 Transport
- 用帳號密碼登入
- 設定網址及建立 Request(必須在登入後,否則會出現 Forbidden Exception)
- 執行 Request 並取得 XML Feed
廢話不多說,以下就是程式碼:
import com.google.api.client.googleapis.GoogleTransport
import com.google.api.client.googleapis.auth.clientlogin.ClientLogin
import com.google.api.client.googleapis.GoogleHeaders
import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpTransport
import com.google.api.client.http.HttpResponse
import scala.xml.XML
import scala.xml.PrettyPrinter
/**
* 取得一個 Feed 的基本步驟
*
* 1. 建立 Transport
* 2. 用帳號密碼登入
* 3. 設定網址及建立 Request(必須在登入後,否則會出現 Forbidden Exception)
* 4. 執行 Request 並取得 XML Feed
*
*/
object GoogleCalendarSample
{
def main (args:Array[String]) {
val username = "username@gmail.com" // 請改成你的帳號
val password = "XXXXX" // 請改成你的密碼
val calendarURL = "http://www.google.com/calendar/feeds/default/owncalendars/full"
val transport = createTransport("My-Calendar-1")
login (transport, username, password)
val request = buildGetRequest (transport, calendarURL)
val response = RedirectHandler.execute(request)
val xml = XML.load(response.getContent)
val xmlPrinter = new PrettyPrinter (100, 4)
println (xmlPrinter.format(xml))
}
/**
* 建立 Transport 以便將來產生 Http Request
*
* @param applicationName 你的應用程式名稱
* @param gdataVersion 要使用的 GData Protocal 版本
*/
def createTransport (applicationName: String, gdataVersion:String = "2"): HttpTransport = {
val transport = GoogleTransport.create()
val headers = transport.defaultHeaders.asInstanceOf[GoogleHeaders]
headers.setApplicationName (applicationName)
headers.gdataVersion = gdataVersion
transport
}
/**
* 進行帳密認證
*
* @param transport 要進行任證的 Transport
* @param username 帳號(GMail 信箱)
* @param password 密碼
* @param gDataType 要使用的 GData 服務代稱,Google Calendar 是 "cl",其他不確定
*/
def login (transport: HttpTransport, username: String, password: String, gDataType: String = "cl") = {
val login = new ClientLogin
login.authTokenType = gDataType
login.username = username
login.password = password
login.authenticate().setAuthorizationHeader (transport)
}
/**
* 建立 Http Request 並設定 URL
*
* @param transport 要使用的 Transport
* @param url 要連線的網址
*/
def buildGetRequest (transport: HttpTransport, url: String): HttpRequest = {
val request = transport.buildGetRequest()
request.setUrl (url)
request
}
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
import com.google.api.client.googleapis.GoogleUrl
import com.google.api.client.http.HttpExecuteIntercepter
import com.google.api.client.http.HttpResponseException
// 下面的這兩個類別是我從 http://0rz.tw/eSQSh 抄來後改寫的,別問我這兩個類別在做什麼,
// 因為我自己也搞不懂。
class SessionIntercepter(transport: HttpTransport, url: GoogleUrl) extends HttpExecuteIntercepter {
var gsessionid: String = url.getFirst("gsessionid").asInstanceOf[String]
transport.removeIntercepters(classOf[SessionIntercepter])
transport.intercepters.add (0, this)
def intercept(request: HttpRequest) {
request.url.set("gsessionid", this.gsessionid);
}
}
object RedirectHandler {
def execute (request: HttpRequest): HttpResponse = try {
request.execute()
} catch {
case e: HttpResponseException if e.response.statusCode == 302 =>
val url = new GoogleUrl (e.response.headers.location)
request.url = url
new SessionIntercepter (request.transport, url)
e.response.ignore
request.execute()
case e =>
throw e
}
}
建立 Transport * 2. 用帳號密碼登入 * 3. 設定網址及建立 Request(必須在登入後,否則會出現 Forbidden Exception) * 4. 執行 Request 並取得 XML Feed

回響