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
回響