diff --git a/2009/04/fontstruct/index.html b/2009/04/fontstruct/index.html index f03bc26f..1ea9ec1c 100644 --- a/2009/04/fontstruct/index.html +++ b/2009/04/fontstruct/index.html @@ -1,6 +1,21 @@ FontStruct:網上自製字型 | EricLog -

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 ZohoGoogle 的一系列服務。這次介紹的是 FontStruct

在 FontStruct 繪製字元「F」

FontStruct 是一個提供網上自製字型的服務。只需要在 FontStruct 免費注冊一個戶口,你就可以使用它以 Adobe Flex 製作的介面來繪製字元。別以為它是很難用,其實用法好像在使用小畫家般。只需要選好預製的形狀,然後在格網上畫上去便行了,網站首頁的影片亦有展示它的使用方法。

畫完了所有需要的字元後,你可以把字型下載成 TrueType 格式 ((TrueType 格式的字型可以在 Windows 及 Mac OS 中使用。)),然後安裝到你的電腦。除了下載給自已用外,FontStruct 還提供了一個平台供會員分享自已使用 FontStruct 製作的字型,你亦可以到 Gallery 下載其他人的作品。

不過 FontStruct 不能提供進階的設定和工具,沒有貝茲曲線 (Bézier curve) ((貝茲曲線可以說是圖像處理軟件的基本功能。)),大大限制了字型的創作。不過對於製作類似 Pixel Font、科幻的字型而言,FontStruct 提供了直覺的介面來製作字型。

comments powered by Disqus
\ No newline at end of file diff --git a/2009/04/ptes/index.html b/2009/04/ptes/index.html index c0aabdec..a7da768e 100644 --- a/2009/04/ptes/index.html +++ b/2009/04/ptes/index.html @@ -6,21 +6,17 @@ /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -">

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:

Microsoft OLE DB Provider for SQL Server error ‘80040e31’

Timeout expired

/tc/search_text/routelist_info.asp, line 134 +">

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:

Microsoft OLE DB Provider for SQL Server error ‘80040e31’

Timeout expired

/tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。

公共交通查詢服務 (PTES) 的界面

不過那天都試用到的(使用打字的方式來標示出發地和目的地),試用過幾次之後,發現了這些問題:

  1. 地圖載入速度太慢 那個地圖功能最好都不要用,因為真是很慢,等了很久還未載入到地圖。地圖的瀏覽方式還是和舊版中原地圖的用法差不多,未有跟隨類似 Google 地圖 / Windows Live Local 的使用和載入方式,導致在瀏覽時都花不少時間來載入地圖。
  2. 在輸入地名搜尋後,會出現同名的項目 @@ -30,8 +26,8 @@ 除了紅色小巴外,邨巴亦沒有提供。
  3. 使用界面設計欠佳 網站仍使用頁框設計,網頁右上角的「路線搜尋」我最初還以以是一個標題,後來才知道它是用來提交的按鈕。另外,一進入網站時會顯示聲明及條款,實在很麻煩。在聲明及條款那頁選了中文之後,到了主頁之後又轉回英文。地圖和搜尋框分開來放實在很不方便。 到了第 2 天才知道系統只能同時容納 1000 人使用,難怪會這麼慢。資料過多而沒有歸納,導致地方名接近。

看看 Transport for London (TfL) 的 Journey Planner,功能比起運輸署的好得多。除了可以選擇交通模式外,還可以設定你自已可接受的步行時間、單車、出發 / 抵達時間,行動不便的人還可以設定你的要求(例如不能使用樓梯、是否使用輪椅)。這方面比起 Google Transit 還好用。不過 TfL 的 Journey Planner 沒有多人同行的車費合計功能,相信這功能亦不難做到,最難的地方都是最短路徑問題 (Shortest path problem)。另一個在 TfL Journey Planner 有的功能是每個方法都會顯示時間,非常方便。

其實政府只需要將這個系統外判給 Google 地圖或者 Windows Live Local 已經大大改善了系統的功能。只不過地政總署本身就想把地圖資料賣給其他人的,並不打算公開讓公眾隨意使用(看看 PTES 的條款以及地政總署測繪處的網頁)。所以也不要想政府會推出 API 讓其他人使用它的資料來做一個更好用的問路系統。不過我覺得外判給 Google 地圖或者 Windows Live Local 做的話 Google / Microsoft 向地政總署付的版權費可能還可以抵消到政府自行開發的費用。因為 Google 本身已經有一套程式來做這些功能,只是因為 Google 沒有香港的資料而沒有提供這服務。(不清楚 Windows Live 有沒有這些功能)如果香港政府肯提供資料的話,我相信 Google / Microsoft 這些大公司亦樂意免費在它們的地圖服務加上香港的交通資料。提資料放上這些地圖網還可以在手機等不同的媒體中使用,而且還有 API。問題只是政府是否願意把這些原本是收費的資料公開。

其實年尾推出供駕車人士使用的問路系統其實亦可以使用 Google 地圖或者 Windows Live Local,問題又是版權問題。

延伸閱讀

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2009/05/pencil-project/index.html b/2009/05/pencil-project/index.html index 03eb41ec..bf36c2e8 100644 --- a/2009/05/pencil-project/index.html +++ b/2009/05/pencil-project/index.html @@ -1,7 +1,7 @@ Pencil Project | EricLog -

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。

Pencil Project 是一個免費繪畫 GUI 界面的軟件,形式和 Visio 差不多。只需要在左邊的 Shape Collection 中拖拉物件到畫布上就可以加入形狀。內置有 GTK+ 和 Windows XP 的 Widget。畫完之後,可以匯出成 PNG 格式的圖像。

Pencil Project 提供 Firefox 附加元件以及獨立桌面版。如果本身已經有安裝 Firefox 的話,可以安裝 Firefox 附加元件版。

comments powered by Disqus
\ No newline at end of file diff --git a/2009/10/google-wave-preview-invitations/index.html b/2009/10/google-wave-preview-invitations/index.html index 79be45c6..0da36072 100644 --- a/2009/10/google-wave-preview-invitations/index.html +++ b/2009/10/google-wave-preview-invitations/index.html @@ -1,7 +1,10 @@ 收到了 Google Wave Preview 的邀請函 | EricLog -

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。

由於目前有太少人可以試用 Google Wave,所以暫時未能提供試用心得,日後將會補上。

Google Wave Preview 的 20 個邀請函
comments powered by Disqus
\ No newline at end of file diff --git a/2009/11/google-wave-review/index.html b/2009/11/google-wave-review/index.html index 1b6657a6..af2ff079 100644 --- a/2009/11/google-wave-review/index.html +++ b/2009/11/google-wave-review/index.html @@ -1,7 +1,12 @@ Google Wave 試用報告 | EricLog -

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。

Wave、Wavelet 和 Blip

Google 將 Google Wave 入面的討論串命名為 Wave。Blip 就是一個個的訊息,亦即是在 Google Wave 對話的基本單位。一個 Wave 之中有不同的 Blip,其他成員可以就個別的 Blip 發表回應(訧像討論區的引用功能般)。那麼上一層的 Blip 就叫做 Wavelet。

即是說:一個 Wave 會有很多個 Wavelet 和獨立的 Blip,而 Wavelet 內有一些 Blip,但亦可以再有 Wavelet。

流暢度和穏定度

Google Wave 經常出現錯誤訊息

Wave 的流暢度很差,即使是用 Google Chrome 瀏覧器都是會在多 blip / 多 gadget 的 wave 時都會出現「lag 機」(不流暢)的情況。在 scroll 和打字的過程中特別明顯。

另外,我在使用 Google Wave 時因使用 MS Office 2007 的新倉頡輸入法而導致當機,亦試過打字期間會自動發送訊息。有一天更經常出現以下的錯誤黑屏訊息:

“Everything’s shiny, Cap’n. Not to fret!” Unfortunately, you’ll need to refresh. Wanna tell Dr. Wave what happened?

重新載入之後,發現清除不到因為自己在剛才所打字時的旗標,即使你再重新發送訊息亦不能清除旗標,要待 Google Wave 自行清除才行。在試用期間亦出現過不能閱讀某個 Wave 的問題,官方討論區亦有不少這個問題的查詢,但解決方法基本上都是要你等,讓 Google Wave 自行回復正常。

功能

Google Wave 目前的功能仍未完全完成,例如與 GMail 通訊錄整合、刪除 Wave 的參與者等。不過一些功能都做得不錯。例如可以即時看到其他參與者打字,即使是用新倉頡 / 新速成等等的智慧型輸入法時未「上字」的文字亦可被其他參與者看到。這個感覺很新奇,不過打字時可能會令人做成壓力(因為打字速度比較慢)。

另外,Google Wave 可以讓參與者在 Wave 中加入 Gadget,這樣就可以為 Wave 增添新的功能。除了 Gadget 外,還有 Robot。Robot 的功能可以有很多,例如自動將空白的 Blip 清走、為內容的表情符號轉成對應的圖片顯示……

總結

Google Wave 目前仍處於 Alpha 階段,基本功能仍有不少未完成,而其他配套亦未夠多。如果不是趕住用的話可以遲些才試用。至於用來做即時通訊軟件,目前而言還是不適合。雖然每個上載檔案最大可以有 20 MB,還可以多重引用。但不能看到其他參與者是否在綫仍是一個大問題。如果 Google Wave 能改善有關問題的話,或許可以取代 Google Talk

comments powered by Disqus
\ No newline at end of file diff --git a/2010/01/light-rail-info-cd/index.html b/2010/01/light-rail-info-cd/index.html index e70cc4a1..20eeb67c 100644 --- a/2010/01/light-rail-info-cd/index.html +++ b/2010/01/light-rail-info-cd/index.html @@ -1,7 +1,7 @@ 輕鐵指南資訊光碟 | EricLog -

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一個路線圖程式。

影片除介紹西鐵通車前的輕鐵歷史外,還有介紹天水圍的兩條新支線及屯門兩個路口架空化改善工程。此外還可以看到輕鐵新舊車身顏色、舊輕鐵接駁巴士站牌、西鐵第一期(屯門至南昌)的模擬片段等。有關影片已經上載到 YouTube:

光碟主頁
輕鐵兆康站相片拼圖
輕鐵路線圖及車費查詢程式
lrt.htz 檔案內容
comments powered by Disqus
\ No newline at end of file diff --git a/2010/03/google-map-street-view-for-hk/index.html b/2010/03/google-map-street-view-for-hk/index.html index 82e375ed..8f8bdbcf 100644 --- a/2010/03/google-map-street-view-for-hk/index.html +++ b/2010/03/google-map-street-view-for-hk/index.html @@ -1,7 +1,10 @@ Google Map Street View 已經登陸香港 | EricLog -

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。

Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環、氹仔及澳門半島南部。拍攝出來的全景圖基本上都沒有大問題,只是有部分的模糊處理有錯。例如將巴士站牌、車身的非車牌部分都被模糊。較為有趣的相信是下圖的例子(位於天城路):

連議員的橫額都被模糊化處理

文章起首提及過我在天壇街看到拍攝車,其實它還在天壇街的露天停車場泊過夜。下圖就是 Street View 在天壇街的露天停車場路段的畫面,留意拍攝位置是在停車場內。

天壇街停車場曾經是其中一個 Google 車的泊車位置

由於香港的拍攝車只是一輛掛上本地車牌和裝了多個鏡頭的私家車,所以進不到邊境和其他禁區拍攝(例如深圳灣大橋愉景灣馬灣;澳門的澳氹大橋)。新的道路亦都未有加入(例如中環龍和道

comments powered by Disqus
\ No newline at end of file diff --git a/2010/06/photoshop-tilt-shift/index.html b/2010/06/photoshop-tilt-shift/index.html index a022abce..a8a63604 100644 --- a/2010/06/photoshop-tilt-shift/index.html +++ b/2010/06/photoshop-tilt-shift/index.html @@ -1,7 +1,21 @@ Photoshop 扮移軸 | EricLog -

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。

相片中白色的輕鐵列車就是新來港的 1117。

Photoshop 扮移軸
comments powered by Disqus
\ No newline at end of file diff --git a/2011/04/data-one/index.html b/2011/04/data-one/index.html index 7048897c..1f90c914 100644 --- a/2011/04/data-one/index.html +++ b/2011/04/data-one/index.html @@ -1,7 +1,10 @@ 資料一線通 | EricLog -

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。

資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。

其實資料一線通就是為開發人員提供 API (Application Programming Interface)。API 是 Web 2.0 的關鍵元素之一,不少網上服務和機構早已開始提供 API 供人免費使用。例如 Google MapsTwitterFacebook倫敦交通局 (Transport for London, TfL) 等。但香港政府和機構就慢人數倍。以運輸署的 PTES 和地政總署的地理資訊地圖為例,網站載入速度太慢、介面設計過時又難用,兩者的使用者經驗與 Google Maps 等相類似的 Web 2.0 服務相差甚遠。唯一的好處可以說是它們的資訊是第一手資料,比起 Google Maps 要用地圖王、MapABC 等二手資料準確得多。現在政府免費提供資料供人使用就可以無須自行開發這些服務網站 / 應用程式。只要有人開發的話,不論是不同平台的電腦、手機、平板還是網上應用程式都可以有。完全不用擔心政府提供的服務追不上科技潮流。

但要擔心的是資料一線通所使用的伺服器能否處理大量的查詢。PTES 初初啟用時經常出現錯誤就已經是一個先例。倫敦交通局在去年初初推出即時地鐵路線服務消息、列車抵站消息、Journey Planner 的 Data Feed 時就出現伺服器負荷過大而要暫停公開 Data Feed。後來轉用 Windows Azure 雲端運算平台才能繼續提供服務。如果資料一線通並非使用如 Azure、Amazon EC2Google App Engine 之類的雲端運算平台的話,很有可能會重蹈 PTES 和 TfL 的覆轍。

最後,緊記填寫資料一線通的網上意見調查,向政府表達在 18 個月的試驗計劃後要繼續免費開放更多資料和 API,方便使用資料的市民和開發人員。

comments powered by Disqus
\ No newline at end of file diff --git a/2011/04/moto-fail/index.html b/2011/04/moto-fail/index.html index 9be81114..c0fe0056 100644 --- a/2011/04/moto-fail/index.html +++ b/2011/04/moto-fail/index.html @@ -1,7 +1,7 @@ Moto Fail | EricLog -

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 Android 2.2 更新會收不到 Google Chrome to Phone 的 Push notification 和 Moto 輸入法的問題。

Milestone 刷了 CM7 的 Home screen

去到 Moto 維修中心,櫃枱上就有一張 Android 2.2 更新的通告。說明 Milestone 用 2.2 會較以前用 2.1 慢和應用程式容易出現 Force Close。不過我還是問那個服務員為甚麼會特別容易 Freeze(Milestone 官方的 2.2 更新幾乎全部地區版本都會有 DSI kernel bug),他說是因為 Milestone 本身 RAM 比起其他用 2.2 的機不夠多,所以特別容易出現這個問題。而 Chrome to Device 用不到的問題就說是其他應用程式的問題,所以不會回應。但我懷疑是 Milestone 官方 2.2 的 C2DM Framework 出問題,因為之前用 CyanogenMod 6 和 CyanogenMod 7 都能收到 Chrome to Phone 的 Push,但用官方 2.2 就永遠都收不到。那個服務員就說會找師傅除塵和更新系統,說要第二天才能取回手機。其實早就知道 Moto 出的 2.2 本身就是爛,所以都不期望更新系統後會得到甚麼改善。

昨日拿回手機後,發現都是亞太版 (SHOLS_U2_05.26.4) 的 2.2,即是說 DSI kernel bug 仍然會出現。回家刷了 CM7 後,由於 Android 2.3 的界面有特別多黑色,發現顯示屏還是有幾點塵,不過比除塵前相比少了很多。

今早無意中看見顯示屏背面突然發現多了道花痕。Milestone 出廠時顯示屏背面是有張保護貼,很多人買回來之後都會保留這張保護貼,並將藍色角位剪走以避免保護貼吸塵。這是因為有不少用家發現多次趟動鍵盤的話顯示屏背面的金屬板會有兩條路軌痕。誰不知 Moto 維修時會再用相信是界刀的工具界多一次那個角位,就連表面黑色油漆都界走。在寫這篇文時又再發現鏡頭的環多了兩道壓痕。

機背的花痕
相機鏡頭的兩道壓痕

其實我早就對 Moto 香港失望,Milestone 的 2.2 更新一拖再拖,拖到有歐洲用家要做一個倒數網站來倒數 Moto 出 2.2 更新的限期。Facebook 的 Moto Europe Fan page 更是充斥着用家的的投訴。Moto 的產品本身就不太差,但市場推廣、售後服務就奇差。Moto 只着重北美及中國內地市場,香港開售的產品比美洲和歐洲都來得遲,來到香港賣都變成「二手科技」。AtrixXOOM 到現在仍未在香港開售,就算開售但其定價還比美國的貴。加上 Android 系統更新比其他品牌還要遲,Milestone 升級 2.2 都要拖成一年(美國的 Droid 就很快有更新),拖了一年的 2.2 還要是 2.2.1 而不是 2.2.2。Moto 自己不更新還要鎖 Bootloader,讓其他社區開發團隊都不能修改核心(如果 Moto 不再出更新的話,DSI kernel bug 將永遠存在),Moto 客戶經理更口出狂言指要刷 ROM 就別買 Moto。現在竟然連維修都有問題!Moto 想靠 Android 來翻身,但第一個翻身作 Milestone 的 2.2 升級就一拖再拖,最後出來還問題多多,令消費者對 Moto 失去信心。真是成也蕭何,敗也蕭何!

comments powered by Disqus
\ No newline at end of file diff --git a/2011/05/download-droid-sans/index.html b/2011/05/download-droid-sans/index.html index 9a47713a..1fdbb0e3 100644 --- a/2011/05/download-droid-sans/index.html +++ b/2011/05/download-droid-sans/index.html @@ -1,9 +1,9 @@ 下載 Droid Sans 字型 | EricLog -

下載 Droid Sans 字型

Droid SansAndroid 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:

  • Droid Sans (regular, bold)
  • Droid Sans Mono (regular, bold)
  • Droid Serif (regular, italic, bold, bold italic) +

    下載 Droid Sans 字型

    Droid SansAndroid 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:

    • Droid Sans (regular, bold)
    • Droid Sans Mono (regular, bold)
    • Droid Serif (regular, italic, bold, bold italic) 同時它亦有黑體中文字型:Droid Sans Fallback。特別之處是運用了 DigiType Compact Asian 技術,將一個包含大量中文字元的字型壓縮至約 3.5 MB 的檔案,非常適合放到手機使用。

    正因為 Droid 字型家族是 Android 所使用的標準字型,在開發 Android Apps、Widget 的時候,很多時都會用縮圖軟件畫一些 Mockup 介面的圖像。如果有 Droid 字型的話就更能得心應手了。就算不用來設計 Android Apps,普通用都不錯。Google 的網站近期都用多了 Droid Sans,不論是 Android Market、今天新開的 Google Music BetaGoogle Apps 還是 Google I/O 的網站,都可以找到 Droid Sans 的蹤影。不過包含中文字型的 Droid Sans Fallback 就因為同時包含繁簡體中文字元的關係,同一個字型入面不同字元的筆畫混合了中港台的寫法,美感不太好。而標點符號就採用了中國內地的規範,全部靠左下角,與香港及台灣要置中的做法有所不同。

    要下載 Droid 字型家族到電腦使用,有幾個方法:

    1. 到 Android 的 Git 下載最新版本
    2. 下載 Android 最新的 SDK
    3. 下載經文泉驛修改的 Droid Sans Fallback——文泉驛微米黑 如果要從 SDK 中尋找 Droid 字型的話,須下載 API Level 11 或以上的 SDK 版本(目前是有 API Level 11 的 Android 3.0 和 API Level 12 的 Android 3.1) ((舊版的 SDK 所提供的 Droid Sans Fallback 是不能夠在 Windows 上使用。))。使用 Windows 的話下載後可以到 C:\Program Files\Android\android-sdk\platforms*android-12*\data\fonts 找到字型(要替換路徑中的 android-12 目錄成你下載的 API Level)。

    另外,如果要在網頁使用的話,Google Web Fonts 亦有提供 Droid SansDroid SerifDroid Sans Mono 的 Web font 供網頁調用。只要在網頁中加載由 Google Web Fonts 提供的指定 CSS 並於網頁的 CSS 檔中設定好那些部分會用到該字型就能調用了。

    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2011/05/general-holidays-for-2012-is-gazetted/index.html b/2011/05/general-holidays-for-2012-is-gazetted/index.html index e93e1c09..2bfa4f33 100644 --- a/2011/05/general-holidays-for-2012-is-gazetted/index.html +++ b/2011/05/general-holidays-for-2012-is-gazetted/index.html @@ -1,7 +1,7 @@ 2012 年公眾假期已刊憲 | EricLog -

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊於 10 月 1 日,所以 10 月 1 日及 2 日都會是公眾假期。

如果你已經在 Google 日曆中訂閱了我的香港公眾假期日曆的話,就不用再重新訂閱。因為 Google 日曆會自動更新及同步到你的其他設備(例如 Android、iOS 設備)。

comments powered by Disqus
\ No newline at end of file diff --git a/2011/08/android-octopus-app/index.html b/2011/08/android-octopus-app/index.html index a034cb33..0d0149af 100644 --- a/2011/08/android-octopus-app/index.html +++ b/2011/08/android-octopus-app/index.html @@ -1,7 +1,7 @@ 八達通手機 App 風波 | EricLog -

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。

其實用小許常識去推斷的話都知道八達通公司自己一定會有一套加密卡內資料的方法來防止被人非法增值,否則其他人一定會自行增值,令八達通公司、運輸機構和其他有使用八達通扣費的機構造成損失,甚至倒閉。而八達通系統並非完全同步的系統,因為地方所限,不是所有的八達通裝置都能夠像櫃員機般能直接駁到八達通公司的中央伺服器來作實時資料同步(例如巴士上的八達通拍卡器),那即是說交易紀錄和目前餘額都會儲存在八達通卡之中。或許這些儲存在卡內的紀錄會在乘搭港鐵出口閘拍卡時會傳送到八達通公司的伺服器作紀錄。所以,八達通公司是一定會有一套加密卡內資料的方法。而八達通公司亦承認八達通是有加密卡內資料:

八達通卡有限公司發言人回應稱,該程式未獲授權認可,公司已調查過,證實不能讀取經加密資料,對卡戶無構成影響。發言人承認,為方便市民查閱,八達通卡內餘額資料未加密,但其他重要資料仍有嚴謹處理及加密,並一直透過全面而嚴密的保安及監控系統監察。

發言人又呼籲市民查詢八達通卡餘額應使用獲公司認可的讀寫器,若因使用未經授權手機應用程式來查詢餘額而招致損失,公司概不負責。

星島日報, 查八達通餘額慎防泄密(2011 年 8 月 8 日)

而後段八達通公司就想向大家推銷八達通公司自家售賣的 USB 讀卡器,原價 $218,特價 $188。要配合其網站的 Java Applet 程式來存取卡內的餘額及交易紀錄,而且只支援 Windows。我記得以前 PCCW 有款多媒體電話亦可以查詢到八達通餘額和交易紀錄,不過到現在還有多少人用這款家用電話就不清楚了。既然八達通公司都證實這個 Android app 是安全(即是只能讀取卡內餘額)的話,大家可以放心使用。除非你對能安在家中都能查到交易紀錄有非常大的需求,否則這個 USB 讀卡器不買也罷。

這個 app 比起之前「搏出位」的八達通「嘟」聲 app 來得更實用。它將香港人日常生活中需要用上的功能做成手機 app,結合目前開始推廣的 NFC 功能。經過今次傳媒的報道後相信會令更多人認識 NFC。只是公眾或許因為先前的無聊「嘟」聲 app 和再之前八達通公司賣客戶私隱一事才忽然緊張起來,擔心卡內私隱泄漏、餘額等資料被刪改。只要用腦想一想就知這是不太可能發生的事,要做的話一早就可以做到,NFC 本身就不是新的東西來,不用等到現在才有。如果這個 app 再進一步美化一下界面的話就完美了!

comments powered by Disqus
\ No newline at end of file diff --git a/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/index.html b/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/index.html index a2e34767..c7daff08 100644 --- a/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/index.html +++ b/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/index.html @@ -1,7 +1,7 @@ 已修正香港公眾假期 Google 日曆權限問題 | EricLog -

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已經設定過權限,如果大家再發現日曆有問題的話請馬上通知,謝謝大家的支持!

comments powered by Disqus
\ No newline at end of file diff --git a/2012/01/ngils/index.html b/2012/01/ngils/index.html index 5301ac35..da141567 100644 --- a/2012/01/ngils/index.html +++ b/2012/01/ngils/index.html @@ -1,7 +1,7 @@ 體驗全新公共圖書館系統 | EricLog -

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 AddThis 按鈕,實在令人難以相信這個是政府做的網站(就算是 gov.hk 都無這個做得出色)。

新世代綜合圖書館系統 (NGILS) 網上圖書館目錄界面

HKPL 除了換網上圖書館目錄之外,原來還開始試用 RFID 技術取代沿用已久的條碼和防盜金屬線。今日去了天水圍北公共圖書館借書,發現全部館藏都被人用箱頭筆點了一點。本以為是盤點的記號,回家後才知道這個記號是代表已貼上 RFID 標籤的館藏。天水圍北公共圖書館是其中一個轉用 RFID 技術的試點之一(相信是因為圖書館面積不大,館藏不多)。跟據公共圖書館諮詢委員會的會議討論摘要,半年後如果效果理想的話會全港推行。

館藏的記號

這個就是貼在書本封底內頁的 RFID 標籤:

書本的 RFID 標籤

而光碟就是這樣的,可以看到還有條碼和防盜金屬線:

借回來的 CD 全部都加上 RFID 標籤

別以為 CD 正中央的白圈是 CD 封面的設計,其實它是 RFID 標籤。

而圖書館內再沒有電腦使用聯機檢索目錄軟件,改為使用上面介紹的網上圖書館目錄。那個陳年聯機檢索目錄軟件設計實在過時,就算將視窗最大化都不能放大顯示搜尋結果清單的範圍。

因應圖書館轉用 RFID,自助借書機都換了全新的白色借書機,再不是之前的 3M 借書機。新借書機的特色是轉用輕觸式屏幕和使用 RFID 技術。使用新借書機都是要先放圖書證到條碼雷射槍的掃描範圍內,然後用輕觸式屏幕輸入密碼,用法和舊機分別不大,但之後的用法就大不同了。因為轉用 RFID,用新借書機不用再逐本找尋條碼的位置,借 CD 又不用打開蓋(以前用借書機借 CD 是很麻煩的:要將 CD 盒打開,如果條碼的方向和條碼雷射槍不同的話要旋轉 CD 來對準雷射槍,借多過一隻 CD 的話就要將這兩個動作重覆)。除了不用找尋條碼,新借書機還可以同時處理最多三項館藏。只需將館藏疊好放在借書機平台的指定範圍內,借書機便會辨認館藏並完成借出手續,完成時會有聲音提示(不再是機械聲)。完成借書手續後可以選擇列印收據,收據比舊借書機的多了幾項資料。例如帳戶借出數量、預約總數、逾期數量等。

全新自助借書機收據,最尾會印出帳戶資料

先前提過的公共圖書館諮詢委員會的會議討論摘要中亦有提及過轉用 RFID 技術後會有廿四小時 RFID 還書箱。而天水圍北公共圖書館內的還書箱亦見有改裝過,相信是在測試廿四小時 RFID 還書箱。明年還會升級公共圖書館的多媒體資訊系統,並會整合網上圖書館目錄。相信日後借書、還書會更加方便。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/03/get-the-latest-snapshot-of-chromium-for-windows/index.html b/2012/03/get-the-latest-snapshot-of-chromium-for-windows/index.html index 3f15850f..2bfcc93c 100644 --- a/2012/03/get-the-latest-snapshot-of-chromium-for-windows/index.html +++ b/2012/03/get-the-latest-snapshot-of-chromium-for-windows/index.html @@ -1,7 +1,7 @@ 下載最新版本的 Chromium for Windows 方法 | EricLog -

下載最新版本的 Chromium for Windows 方法

ChromiumGoogle Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。

不過 Chromium 的每日 snapshot 下載位置愈來愈神秘,在 Chromium 的網站中根本就找不到,在 Google 搜尋到的網站都是介紹過時的下載位置。幸好 François Beaufort 用 Python + Google App Engine 做了個網站方便大家下載最新的 Chromium for Windows。進入網站後按一下「Click here to download Chromium for Windows」就可以下載到即日的 Chromium snapshot。

其實我下載 Chromium 到 USB 手指主要是為了用 Chrome/WebKit 的開發人員工具,這個工具和 Firefox 的 Firebug 用法差不多。不過 Chrome/Chromium 那個就不用另外安裝 Extension,而且 Chromium 本身下載回來就是免安裝的,所以就選了它放入 USB 手指方便在學校用。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/04/general-holidays-for-2013-is-gazetted/index.html b/2012/04/general-holidays-for-2013-is-gazetted/index.html index c2459511..c2692d17 100644 --- a/2012/04/general-holidays-for-2013-is-gazetted/index.html +++ b/2012/04/general-holidays-for-2013-is-gazetted/index.html @@ -1,7 +1,7 @@ 2013 年公眾假期已刊憲 | EricLog -

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是下載 iCal 檔的話就需要再下載並匯入香港公眾假期日曆過。

2013 年的農曆年初一由於撞正星期日,所以年初四會有補假。而重陽節因為同樣原因而在星期一設有補假。

詳細的公眾假期清單可參閱香港政府一站通網頁。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/05/heatsink/index.html b/2012/05/heatsink/index.html index 70f31190..777695c6 100644 --- a/2012/05/heatsink/index.html +++ b/2012/05/heatsink/index.html @@ -1,7 +1,37 @@ 散熱器新用法 | EricLog -

散熱器新用法

舊電腦有個 Intel Pentium 4 原裝散熱器還未用過,非常新淨。拉絲鋁的材質看起來很前衛,拿來當信座都不錯。

信座正面

做法非常簡單:

  1. 將散熱器上的風扇拆走
  2. 在底部貼上雙面膠紙
  3. 貼上軟膠墊來防止散熱器刮花桌面
  4. 將信件插入散熱片之間的空隙即可

軟膠墊我是在先前買散裝 DVD 燒碟機的保護膠套中裁出來的。其實還可以用沙紙將邊角位磨到圓滑避免刮手。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/08/official-octopus-app/index.html b/2012/08/official-octopus-app/index.html index 4686e014..d80888e3 100644 --- a/2012/08/official-octopus-app/index.html +++ b/2012/08/official-octopus-app/index.html @@ -1,7 +1,7 @@ 八達通查閱易 | EricLog -

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。

要查閱八達通卡的資料,要先登記八達通卡編號。只有登記過的八達通卡方可查閱。登記後,將有 NFC 功能的手機放到八達通卡上,app 就會到八達通公司的伺服器下載該卡的餘額及最近十次的交易紀綠並顯示在手機屏幕上。三十秒後就會從屏幕自動消失。如果要再查閱另一張卡的話,要先按 Back 鍵返回上一頁。

八達通查閱易的最近十次交易紀錄

由於用法和先前在 Play Store 發佈的八達通 app 相比實在太麻煩,所以有不少負面評價。主要在於查閱餘額要經互聯網而非直接解讀卡內的資料和預先登記卡號。卡內其實已經有卡號,要人預先填寫確實多餘。另外,如果知道卡號的話八達通公司一定可以查到卡的餘額和交易紀綠,所以要再用 NFC 拍卡亦都是多餘。但相信八達通公司日後不會將查閱功能改為直接解讀,因為怕被人反編譯(Android 的應用程式主要是用 Java 寫的,像 Java 這些需要編譯兩次的程式都是容易被人反編譯)。而要預先登記卡號是為了防止他人未經卡主同意就拍卡讀取資料(例如在街上用手機拍一下口袋來知道你卡內的餘額)。自從八達通日日賞賣私隠一事之後,估計八達通公司都不會收回預先登記、三十秒顯示的設定,生怕又被公眾投訴私隠外洩,再度影響公司形像。

我自己用過幾次,決定不會再用其他八達通 app。只因為它可以讀取到最近十次交易紀錄,但郤發現乘搭港鐵巴士的紀錄都變成輕鐵。如果日後會加入儲存紀錄功能就更加好。

八達通公司這次推出 Android app 旨在向外界宣揚它造的 app 才是正統,希望大家不要再用其他的 app 來讀取卡內資訊。但卻令到它本身有出售的 USB 讀卡器變得無用(那個讀卡器同樣是要連接到八達通公司的伺服器來讀取資訊)。但它肯推出手機 app 已經算是與時並進了。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/09/lenovo-thinkpad-x230/index.html b/2012/09/lenovo-thinkpad-x230/index.html index 40264408..a9b45a8d 100644 --- a/2012/09/lenovo-thinkpad-x230/index.html +++ b/2012/09/lenovo-thinkpad-x230/index.html @@ -1,7 +1,7 @@ Lenovo ThinkPad X230 | EricLog -

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。

Lenovo 方面當時有考慮過三部:

  1. IdeaPad U410(山寨版 MacBook Pro)
  2. ThinkPad Edge S430
  3. ThinkPad X230

最初是考慮過 U410,因為它有獨顯和 SSD。但再 Google 一下時就發現 U310/U410 的 Wi-Fi 設計有問題,導致 Wi-Fi 接收不佳。加上外觀設計太山寨,B 面和 C 面(Bezel 和 Palm rest)和 MacBook Pro 驟眼看實在太相似,最後都是放棄。之後有考慮過 ThinkPad Edge S430。重量約 1.8 kg,有 DVD 燒碟機,但是只有內置電池。加上一直都有人指出 Lenovo 只有傳統的 ThinkPad X/T/W 型號的品質才靠得住。最後拍板都是要 ThinkPad X230。

Lenovo ThinkPad X230

X230 機身比較細小,尺寸和一張 A4 紙差不多。和以前的 ThinkPad 相比,最大的分別在於鍵盤。X230 是用孤島式鍵盤 (Island-style keyboard / Chiclet keyboard) 而不是傳統鍵盤。第二個分別是 X230 採用六列鍵盤而非以往的七列鍵盤。這兩個改動引來大量 ThinkPad 用家的不滿,有人甚至將 X220 的傳統鍵盤裝到 X230 上。由於這是我第一部 Notebook,之前很少會用 Notebook,對於 ThinkPad 舊鍵盤沒有太深刻的印象,所以沒有那些老用戶的包袱。新鍵盤感覺良好,很彈手。除了頂頭的 ThinkLight 之外還有鍵盤背光燈,非常吸引。不過這個鍵盤並沒有 Fn + Numpad、用 Fn 鍵停用 Touch Pad和 Caps Lock 提示燈(改為 On-screen Display),似有 Cut Cost 的感覺。

ThinkPad 的另一大特色是「中原一點紅」,試用過 TrackPoint 覺得非常適合文書工作,可以在雙手不用離開鍵盤下使用滑鼠(例如轉字型 / 顏色之類)。但一般上網之類還是用回 Touch Pad 比較方便。Touch Pad 支援 Multi-touch gesture,但因為 Touch Pad 面積細小在使用上不如 MacBook Pro / MacBook Air 那個 Touch Pad。

Lenovo ThinkLight
Lenovo 背光鍵盤
屏幕下的指示燈都已經被 Cut Cost,只有 Wi-Fi 和硬碟燈,沒有 Caps Lock 燈

硬件方面,12.5" (1366 × 768 px) TN Panel 顏色普通,顏色偏藍。後來網上找到有其他人造的 ICC Profile,套用後效果順眼了不少。字體大小方面尚可接受,解像度方面都不可能再加大,再加大字體就更加細小,完全不能使用。X230 使用 6-cell 電池大約可以用到 5 至 6 小時,夠「長氣」。這個是我選擇 X230 的主要原因之一。X230 更換硬碟和 RAM 很方便,只需要拆開對應的蓋就可以換到,不需要拆機。試過沒有問題之後就加多一條 4GB RAM,變成 8GB RAM。日後可以升級硬碟做 SSD(要用 7 mm)。

加 RAM 很方便,只需要打開 RAM 槽蓋就能加到

軟件方面,OS 只是 Windows 7 Home Premium 而不是 Windows 7 Professional。Recovery Media 只可以做一次,之前透過修改檔案來解除限制的方法已經失效。跟機附有小量軟件,例如 Google Chrome、Skype、SugarSync、Office Starter 2010 和 ThinkVantage,相比起其他牌子算是少 Boltware。Office Starter 是閹割版的 Office,只有 Word 和 Excel,右邊有廣告,功能都比完整版 Office 少。好處只有一個--免費。不過我還是換做完整版的 Office。

而 ThinkVentage 是 ThinkPad 內附的一系列軟件,主要是用來管理電腦。例如備份、更新、電池、硬碟震動保護等。其中 Wi-Fi 軟件會以圖像方式顯示熱點,可以看到 TP-LINK 的 Router 是熱門品牌(我自己都是用 TP-LINK)。

另一個軟件叫 SimpleTap,只需要按下鍵盤的 ThinkVentage 掣就會進入 SimpleTap 界面。界面的設計像平板電腦,有一些網上服務捷徑、ThinkVantage 工具捷徑和硬件設定(開關 Wi-Fi、音量、光暗度等)。但在沒有 Touch screen 的電腦使用效果很差,最後我都刪除了它。

總體而言,除了屏幕尺寸比較細之外,基本上都很滿意。等到 SSD 價格再創新低時就可以換做 SSD。

comments powered by Disqus
\ No newline at end of file diff --git a/2012/09/sony-xperia-ion/index.html b/2012/09/sony-xperia-ion/index.html index ad071a35..dc6fe343 100644 --- a/2012/09/sony-xperia-ion/index.html +++ b/2012/09/sony-xperia-ion/index.html @@ -1,9 +1,9 @@ Sony Xperia ion | EricLog -

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。

Xperia ion 及 SmartDock 包裝盒

購機少不免都要提及一下贈品,贈品其實都算實用:

  1. 手機保護殼
  2. SmartDock
  3. HDMI 線
  4. Sony 外置充電器(2000 mAh,只可以充到手機一半的電量左右)
  5. 毛巾

主要優點

  1. 外觀設計精美,背面有黑色拉絲金屬面
  2. 屏幕有 4.6",密度超過 300 dpi (XHDPI)
  3. 相機鏡頭有 1200 萬像素,有一掃全景功能,有實體快門掣,還可以設定相機無快門 / 對焦聲,而且由待機到拍攝可以在兩秒完成
  4. Zero-air-gap display,玻璃和 Panel 非常貼近
  5. 屏幕下方有齊「四大天王鍵」(即 Back、Home、Menu、Search)
  6. 可加插 MicroSD 卡,內置相機 app 可以設定拍攝相片 / 影片儲存到 MicroSD 卡而非內置存儲空間
  7. 有 NFC 功能
  8. 有 FM 收音機功能

主要缺點

  1. 內置電池,不能換電
  2. USB 和 HDMI 有蓋防塵,但容易爛,充電時需小心開合
  3. 音量細,開了 xLoud 還是細,而且容易「拆聲」
  4. 不能設為自動調節屏幕背光燈光暗(其實它會自動調節,但細心觀看會看到屏幕背光燈頻頻閃動,非常討厭)
  5. 「四大天王鍵」感應很差,若果在「四大天王鍵」稍加施力按下屏幕會有水波紋,可以感受到整個屏幕不是玻璃硬屏,而且下方的橫條照明燈設計失敗,應該將燈用於照明按鍵的圖案
  6. 耗電量高,內置的電池如果想全日不充電有困難
  7. LTE 制式不支援香港
  8. 出廠還是使用 Android 2.3.7,而非 ICS(Sony 聲稱會在今季出 ICS 更新)
  9. ROM 有 bug,不知會否在 ICS update 中修正 +

    Sony Xperia ion

    七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。

    Xperia ion 及 SmartDock 包裝盒

    購機少不免都要提及一下贈品,贈品其實都算實用:

    1. 手機保護殼
    2. SmartDock
    3. HDMI 線
    4. Sony 外置充電器(2000 mAh,只可以充到手機一半的電量左右)
    5. 毛巾

    主要優點

    1. 外觀設計精美,背面有黑色拉絲金屬面
    2. 屏幕有 4.6",密度超過 300 dpi (XHDPI)
    3. 相機鏡頭有 1200 萬像素,有一掃全景功能,有實體快門掣,還可以設定相機無快門 / 對焦聲,而且由待機到拍攝可以在兩秒完成
    4. Zero-air-gap display,玻璃和 Panel 非常貼近
    5. 屏幕下方有齊「四大天王鍵」(即 Back、Home、Menu、Search)
    6. 可加插 MicroSD 卡,內置相機 app 可以設定拍攝相片 / 影片儲存到 MicroSD 卡而非內置存儲空間
    7. 有 NFC 功能
    8. 有 FM 收音機功能

    主要缺點

    1. 內置電池,不能換電
    2. USB 和 HDMI 有蓋防塵,但容易爛,充電時需小心開合
    3. 音量細,開了 xLoud 還是細,而且容易「拆聲」
    4. 不能設為自動調節屏幕背光燈光暗(其實它會自動調節,但細心觀看會看到屏幕背光燈頻頻閃動,非常討厭)
    5. 「四大天王鍵」感應很差,若果在「四大天王鍵」稍加施力按下屏幕會有水波紋,可以感受到整個屏幕不是玻璃硬屏,而且下方的橫條照明燈設計失敗,應該將燈用於照明按鍵的圖案
    6. 耗電量高,內置的電池如果想全日不充電有困難
    7. LTE 制式不支援香港
    8. 出廠還是使用 Android 2.3.7,而非 ICS(Sony 聲稱會在今季出 ICS 更新)
    9. ROM 有 bug,不知會否在 ICS update 中修正 Sony / Sony Ericsson 的手機設計向來都別具一格,機背的鎂鋁拉絲金屬非常吸引。USB 和 HDMI 埠都用蓋收起,保持外觀的連貫性。但這個設計在使用 SmartDock 時並不如 Milestone 方便,而且保護蓋會否因長期使用而脫落還是未知之數。
    Xperia ion 正面
    Xperia ion 背面

    4.6" 的大屏幕亦是吸引我的地方。由於屏幕過了 300 dpi,文字 / 圖像「起格」的清況比之前的 Milestone 更難看到,要非常仔細才能看到。

    軟件方面,Sony 在界面設計下了一番功夫。手機可以切換不同的顏色主題。部分軟件不是使用 Android 原裝,而是自己改裝過。而且在橫向屏幕是介面還會特別設計過,尤其是音樂播放和電郵。

    音樂界面重新設計,可以在網上下載音樂資訊來為音樂分類 (SensMe channel) 、Cover art 和顯示歌手相片。介面有一些圓形的 icon,似乎抄襲 Windows Phone 7 的界面設計。

    內置一個名為 TrackID 的 app,是用來辨認歌曲。可以辨認外界音樂,亦可以辨認內置 FM 收音機所播放的音樂。但辨認效果比其他 app 差,它只能辨認唱片的音樂。清唱、哼歌都認不到。

    電郵 app 和原裝 Android 那個基本上差不多,但橫向時會有類似平板的顯示方式:左邊顯示電郵清單;右邊顯示電郵內容,而且可以左右拉動改變兩邊的大小。

    另一個值得一提的功能是 NFC。自從第二部 Google 「親生仔」Nexus S 開始,近期新推出的手機都開始加入 NFC 功能。NFC 除了可以用來查閱八達通餘額之外,Sony 為其推出的 NFC 手機配售「Xperia SmartTag」及預置 SmartTag 軟件。當手機碰到 SmartTag 時,手機就會切換到另一個場景,例如碰到黑色 SmartTag 時就會切換到「臥室」場景,手機會關閉 Wi-Fi、靜音和開啟鬧鐘 app。想用 SmartTag 功能是可以不用買 Sony 的 SmartTag 的,只需要找一些沒有用的 RFID Tag 就可以做到。我亦用了一些用完的廣深線車票寫入以下的 URL 就做了山寨版 SmartTag。

    • 藍色標籤 semc://liveware/A1/1/NT1/1/smarttags1
    • 紅色標籤 semc://liveware/A1/1/NT1/2/smarttags1
    • 黑色標籤 semc://liveware/A1/1/NT1/3/smarttags1
    • 白色標籤 semc://liveware/A1/1/NT1/4/smarttags1
    Xperia SmartTag
    用廣深線動車 RFID 車票當作 Xperia SmartTag

    相機是 Xperia ion 的重點功能,因為整部 Xperia ion 的賣點就是相機、大 Mon 和 Design。這個 1200 萬像素相機拍攝效果不俗,普通的拍攝可以取代數碼相機。跟機的相機 app 備有多項功能和設定,和普通的數碼相機差不多。

    Xperia ion 實拍:彩虹

    Xperia ion 的介紹到這裏為止。以下是 Xperia ion 目前 Android 2.3.7 的 bug:

    1. 屏幕背光燈轉光 / 轉暗時轉變過程慢,令到背光燈不斷閃動(這個可能要在白色背景的畫面細心觀看才會看到)
    2. 在完成電話通話後,如果對方先過自己中斷通話會有機會令手機當機,幾秒後會自動重新開機(不是每次都出現,但我試過兩次)
    3. 耳機拔除後手機可能不能感應得到,以為手機還插着耳機,結果啦叭無聲,要再次插入及拔出耳機後才回復正常 之前已將電話拿取旺角銀城要求 Sony 維修,但最後只是拆機檢查天線,發現正常然後刷機了事,並沒有認真解決問題。問題的根源是 ROM 有 bug,檢查手機天線是捉錯用神,重刷多次 Android 2.3.7 更是不知所謂。本身 ROM 有問題郤走去重刷一個有問題的 ROM 是不可能解決問題。當然,拿去維修前早就預計到他們會重刷了事。我拿機去維修只是想讓他們知道 ROM 是有問題,現在唯有希望他們日後的更新會修服以上問題,同時亦期望會有更多非官方的 ROM 可以刷,以免太快變成「孤兒機」。

    總括而言,我還是喜歡我的 Xperia ion,期望 Sony 會盡快改善。

    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2012/12/facebook-for-android-updated/index.html b/2012/12/facebook-for-android-updated/index.html index 40788505..74d550a2 100644 --- a/2012/12/facebook-for-android-updated/index.html +++ b/2012/12/facebook-for-android-updated/index.html @@ -1,7 +1,7 @@ Facebook for Android 出了更新了! | EricLog -

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。

如果你的 Android Facebook app 界面是以簡體中文顯示的話,請檢查你的設備是否使用「中文(香港)」、「中文(香港特別行政區)」之類的語言。如果是的話,請切換到「中文(繁體)」、「中文(台灣)」之類的語言,問題就會迎刃而解。

使用 zh_TW 時會以繁體中文顯示

為甚麼使用「中文(台灣)」就沒有問題?**原因是 Android 根本就沒有 zh_HK 這個語言,zh_HK 只是一眾手機平板廠商自作聰明加上去的。**Android SDK 的 emulator 都只有「中文(繁體)」和「中文(簡體)」。但廠商自行加了 zh_HK 後,郤沒有做好如果 app 沒有 zh_HK 的界面語言的話要怎樣 fallback。結果用戶將設備語言設成「中文(香港)」後,只有原生的 app 才會出現繁體中文界面,其他 app 甚至是內置的 Google apps(例如 Google Play、Gmail)只會出現英文界面。Sony 更將中文輸入法與語系綑綁,如果要輸入香港字的話,就必須要將系統語言設為「中文(香港)」,使用「中文(台灣)」就不能輸入。

Sony Xperia ion 的語言設定

正因為 Android 本身就沒有 zh_HK,相信只有香港的 Android app developer 才會知道香港用戶有機會會使用 zh_HK 這個語系,開發 app 時會加上 zh_HK 界面語言,所以大多數的 Android app 就算有配上 zh_TW 的界面語言,如果用戶將自己的系統語言設成 zh_HK 的話都很可能只看到英文,又或者會像 Facebook app 一樣出現簡體中文。

可能當時 Android 的開發團隊當時未有考慮到部分地區例如香港會使用多過一種語言,不像美國般只用英文,所以將系統的地區和語言合併來設定。如果系統將地區和語言是分開來設定的話,那就不會出現今日 Facebook app 的情況,廠商又不用自作聰明地加上 zh_HK 了。在目前的情況,如果想用繁體中文介面的話,我還是建議大家用 zh_TW,不要再用 zh_HK。(我自己都是用 zh_TW 的)

12 月 15 日補充:本地 Android app 開發者小倫在其網誌小倫角落分享了如何整理 Android app 的界面語言。

comments powered by Disqus
\ No newline at end of file diff --git a/2013/01/outlook-exchange-activesync/index.html b/2013/01/outlook-exchange-activesync/index.html index eaf56072..22f14ba0 100644 --- a/2013/01/outlook-exchange-activesync/index.html +++ b/2013/01/outlook-exchange-activesync/index.html @@ -1,8 +1,8 @@ 使用 Exchange ActiveSync 同步 Outlook.com 郵箱 | EricLog -

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。

以下圖片所顯示的界面可能會因為設備生產廠商不同而和你的設備有所出入,但應該大同小異。

首先在電郵 app 裡新增一個帳戶。輸入你的 Outlook.com / Hotmail 電郵地址和密碼,然後按「手動設定」。(留意不是按「下一步」,因為按「下一步」是會用 SMTP/POP 收發電郵。)

輸入電郵地址和密碼

下一步就是按「Exchange ActiveSync」。

揀選「Exchange ActiveSync」

之後會轉到內收設定頁,它自動替你填上設定。但我們是需要改動它們才可以用 Exchange ActiveSync。

  • **網域\使用者名稱:**你的 Outlook.com / Hotmail 完整的電郵地址(不是電子郵件別名)
  • **密碼:**你的登入密碼
  • **伺服器:**m.hotmail.com +

    使用 Exchange ActiveSync 同步 Outlook.com 郵箱

    微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。

    以下圖片所顯示的界面可能會因為設備生產廠商不同而和你的設備有所出入,但應該大同小異。

    首先在電郵 app 裡新增一個帳戶。輸入你的 Outlook.com / Hotmail 電郵地址和密碼,然後按「手動設定」。(留意不是按「下一步」,因為按「下一步」是會用 SMTP/POP 收發電郵。)

    輸入電郵地址和密碼

    下一步就是按「Exchange ActiveSync」。

    揀選「Exchange ActiveSync」

    之後會轉到內收設定頁,它自動替你填上設定。但我們是需要改動它們才可以用 Exchange ActiveSync。

    • **網域\使用者名稱:**你的 Outlook.com / Hotmail 完整的電郵地址(不是電子郵件別名)
    • **密碼:**你的登入密碼
    • **伺服器:**m.hotmail.com 然後將「使用安全連線 (SSL)」打勾,如下圖所示:
    輸入使用者名稱、密碼和伺服器

    完成後按「下一步」,再按照程式的指示完成餘下的步驟就可以新增你的 Outlook.com / Hotmail 郵箱了。

    使用 Exchange ActiveSync 除了可以用 Push 的方式接收新郵件外,還可以同步 Outlook.com / Hotmail 的通訊錄和行事曆。不過我只是用來同步郵箱,沒有試過同步我的 Outlook.com 通訊錄和行事曆,相信是可以同步到的。除此之外,使用 Exchange ActiveSync 可以免郤安裝 Outlook.com 專用 app,那個 app 的界面非常普通,還是維持住 Android 2.3 的設計。如果你需要接收其他郵箱(例如 Yahoo、ISP)的話,使用內置的電郵程式可以在合併收件箱閱讀所有郵件,郵件會按時間排列好。你還可以為不同的帳號設定好代表顏色,方使在合併收件箱辨認。

    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2013/01/trackpoint-middle-click/index.html b/2013/01/trackpoint-middle-click/index.html index a797c0df..d6bd4f94 100644 --- a/2013/01/trackpoint-middle-click/index.html +++ b/2013/01/trackpoint-middle-click/index.html @@ -1,7 +1,19 @@ TrackPoint 使用滑鼠中鍵 | EricLog -

TrackPoint 使用滑鼠中鍵

TrackPoint

Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。

原來有人寫了一個程式叫 TPMiddle,只需要下載它並放到 Start Menu 內的 Startup 資料夾就可以令到 UltraNav 的中鍵同時可以做到頁面滾動和滑鼠中鍵的功能。現在想開新分頁 / 關閉分頁的話只需要按一下中鍵就可以了;想滾動頁面的話就按住中鍵並在 TrackPoint 施力就可以了。

comments powered by Disqus
\ No newline at end of file diff --git a/2013/03/simcity/index.html b/2013/03/simcity/index.html index b75590cf..500e32d9 100644 --- a/2013/03/simcity/index.html +++ b/2013/03/simcity/index.html @@ -1,7 +1,7 @@ SimCity | EricLog -

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。

不過送遊戲很可能是谷銷量的手段,最少我都是因為它送遊戲才買。到現在還有不少 bug 和問題讓 EA/Maxis 修正,我自己也中了幾個:

  1. 伺服器有時會連不上、城市不能載入,只會停留在載入畫面(現在應該少了,因為它不斷加開)
  2. 部份大廈會自己閃動
  3. 部分大廈的底部會自己多了個土堆
  4. 城市會突然不能進入,在區域中揀選城市時會出現「城市目前正在處理中。請稍待片刻再拜訪或遊玩。」訊息,而該城市在區域 / 其他城市中看的話會一幢樓都看不見,就像未開發般
  5. 汽車 pathfinding 有問題,只會揀選最短距離的路線而不考慮塞車(現在開始會修服

就因為第 4 個問題,令到我和我的朋友要再開一個新的地圖重玩。雖然 SimCity 現在問題多多,但有幾個地方做得不錯:

  1. 警察局、消防局、診所、學校等建築的有效範圍不再以圓形覆蓋,現在一間建築可以全市使用,效果取決於車輛能否及時到達事故現場和建築本身的容量是否達到上限
  2. 同一張地圖的城市可以共用和買賣資源,例如政府部門、大學研究成果、水、電等等,還可以送錢去其他城市
  3. 空氣污染會跨城,不會再像 SimCity 4 般只會影響自己一個城市
  4. 火警發生的頻率比 SimCity 4 高很多,以往甚少會出現,現在每次玩都可以見到
  5. 建築可以有附加建築 / 部件,可以先擴建而不用多建一個新的
  6. 可以興建彎路(包括橋和隧道)

如果不計算先前提過的 bug 之外,有幾點我是不太喜歡:

  1. 城市面積太小,太快用完城市的用地
  2. 沒有地形工具,不能移山填海
  3. 出入城的高速公路路口很多時只有一個,而且不可以加建
  4. 畫風比起 SimCity 4 太卡通化,SimCity 4 比較像真
  5. 不能建造地下行車道,多層高架橋

照目前其他玩家提供的心得和攻略,最能賺錢的方法是「CPU + 電視 + 貿易」模式。不論是這個模式還是其他模式,最重要的是確保城市的道路交通暢通。因為很多東西都和路面交通相關:警察、消防、醫療、教育、貿易、工作等等。只要交通暢通,城市的問題都會減少。

SimCity 包裝盒正面
SimCity 包裝盒背面
連線中斷在開售初期可謂家常便飯
SimCity bug 之一:大廈會自己升高
SimCity bug 之一:城市不能進入
發展中的城市
城市一景
SimCity 現在可以看到區域的其他城市
天空的飛機
交通擠塞
SimCity 內有不同的濾鏡
SimCity 內的濾鏡,令畫面變得古舊
SimCity 內的濾鏡,令畫面變得蒼白
UFO 侵襲
龍捲風
城市多處發生火警
一片火海
限量版附送的 V 博士大樓和 Maxis 超人宅邸
Maxis 超人宅邸
V 博士大樓天台

comments powered by Disqus
\ No newline at end of file diff --git a/2013/05/bitbucket-free-academic-license/index.html b/2013/05/bitbucket-free-academic-license/index.html index accf535d..2b50e3a5 100644 --- a/2013/05/bitbucket-free-academic-license/index.html +++ b/2013/05/bitbucket-free-academic-license/index.html @@ -1,7 +1,7 @@ Bitbucket 免費 Academic License | EricLog -

Bitbucket 免費 Academic License

BitbucketGitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話可以考慮用 Bitbucket。Bitbucket 最多可以與五個人共用私人的 repo,但它有推薦制度來增加共用名額,最多能與八個人共用同一個私人 repo。

如果想再增加使用者而你又有大學電郵地址的話,可以免費申請 Bitbucket 的 Academic License。Academic License 可以將 repo 與無限人共用私人 repo。只需在表格填上自己 Bitbucket 帳戶資料、大學資料就可以了。留意自己的 Bitbucket 帳戶是要將表格所填的大學電郵設定帳戶預設的電郵,否則不能升級。如果你平時並不是用大學電郵地址來 commit 的話,你可以再加上其他電郵地址,這樣就不會在 Bitbucket repo 的網頁介面顯示提交人為 Unknown,而是顯示為你的帳戶。

成功升級 Academic License
comments powered by Disqus
\ No newline at end of file diff --git a/2013/06/laraval-4-chinese-translation/index.html b/2013/06/laraval-4-chinese-translation/index.html index 345a8acf..1c3635c6 100644 --- a/2013/06/laraval-4-chinese-translation/index.html +++ b/2013/06/laraval-4-chinese-translation/index.html @@ -1,7 +1,7 @@ Laraval 4 中文語系翻譯 | EricLog -

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。

comments powered by Disqus
\ No newline at end of file diff --git a/2013/06/laravel-4/index.html b/2013/06/laravel-4/index.html index f4bbf332..f238dda0 100644 --- a/2013/06/laravel-4/index.html +++ b/2013/06/laravel-4/index.html @@ -1,9 +1,9 @@ Laravel 4 | EricLog -

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。

所以那時就試用 CodeIgniter 這套 PHP framework 來做 project,因為這些 Framework 都會替你分好 MVC,不用自己慢慢研究怎樣分,只要跟着它的習慣去寫就不會再有一個 PHP 檔做全部的動作。 過了幾個月之後,在 Nettuts+ 看到有關 Laravel 的介紹,覺得它不錯。它提供的 function 就算不看說明文件也可以大約估計到它的意思,而且說明文件簡單明瞭,大部分要用到的功能都會在說明文件中找到,不用查 API。據介紹 Laravel 是抄 Ruby on Rails 的特色,例如有:

  • ActiveRecord:即是 Laravel 叫的 Eloquent ORM (Object Relational Mapping),可以用簡單易明的語法來做 database CRUD,無需寫 SQL statement
  • Migration:將 Database schema 的改動寫入 PHP 檔內,再用 Laravel 提供的 Artisan CLI 工具執行或者還原到上一個版本。方便和其他開發人員同步各自 database 的 schema,不用再 export SQL file
  • Generate:透過 Laravel 4 的 Artisan CLI 工具產生 PHP 檔案(例如 Controller class、Model class、View 等等,Laravel 3 可以裝第三方的 command 來補上這個功能)
  • Gems:Laravel 3 是用自家制式的 Bundle;Laravel 4 是用 Composer
  • RESTful:可以做到 RESTful controller,Laravel 4 還新加了 Resource controller +

    Laravel 4

    我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。

    所以那時就試用 CodeIgniter 這套 PHP framework 來做 project,因為這些 Framework 都會替你分好 MVC,不用自己慢慢研究怎樣分,只要跟着它的習慣去寫就不會再有一個 PHP 檔做全部的動作。 過了幾個月之後,在 Nettuts+ 看到有關 Laravel 的介紹,覺得它不錯。它提供的 function 就算不看說明文件也可以大約估計到它的意思,而且說明文件簡單明瞭,大部分要用到的功能都會在說明文件中找到,不用查 API。據介紹 Laravel 是抄 Ruby on Rails 的特色,例如有:

    • ActiveRecord:即是 Laravel 叫的 Eloquent ORM (Object Relational Mapping),可以用簡單易明的語法來做 database CRUD,無需寫 SQL statement
    • Migration:將 Database schema 的改動寫入 PHP 檔內,再用 Laravel 提供的 Artisan CLI 工具執行或者還原到上一個版本。方便和其他開發人員同步各自 database 的 schema,不用再 export SQL file
    • Generate:透過 Laravel 4 的 Artisan CLI 工具產生 PHP 檔案(例如 Controller class、Model class、View 等等,Laravel 3 可以裝第三方的 command 來補上這個功能)
    • Gems:Laravel 3 是用自家制式的 Bundle;Laravel 4 是用 Composer
    • RESTful:可以做到 RESTful controller,Laravel 4 還新加了 Resource controller 再加上一堆常用的現成功能如 HTML 表單製作 helper class、表單驗証、登入系統、Blade template(Laravel 自家 Templating 格式)、Pagination、Routing 等等令開發時間加快。上月開站的 CityU GE 指南都是用 Laravel 3 寫的,再配 Twitter BootstrapFont Awesome 做 UI。

    而 Laravel 4 (L4) 在上星期推出了 4.0.0 版,這個版本基本上是重新寫過。亦正因為它是重新寫過,大部分 Laravel 3 (L3) 的 Class 和 Function 都改了,如果要升級的話就要逐個 PHP 檔案改成 L4 的 Class 和 Function。除了加添更多新功能外,L4 最大的改變是轉用 Composer 來管理組件,整個 Framework 都是由不同的組件組合而成。如果不喜歡 Laravel 的作法可以裝原裝組件換成自己喜歡的,例如將 Blade 換做 Smarty。而 L4 本身亦用上了其他現成的組件,例如 Symfonywhoops!(L4 那個很漂亮的錯誤報告畫面就是用這個 Library 做出來的)、Swift Mailer 等等。組件的安裝和更新可以用 Composer 替你完成,不用再自己手動換檔案。另一樣比較明顯的變化是 L4 遵守 PSR-0 and PSR-1 標準,Class 名要用 StudlyCaps、Function 名要用 camelCase,和新的 PHP 5 class 或者 Java 一樣。幸好 CityU GE 指南的規模不大,三兩日就可以將原先的 code 升級成 L4。首先要大改的就是將原先的 snake_case variable / function / class 名按照 PSR-1 標準替換。其次是將 class / function 名變成 L4 的(因為有不少常用的 function 都改了 signature)。之後再到 Packagist 找尋 reCAPTCHA、RSS feed 和 Sitemap class 的組件取代原先的 L3 bundle,並用 Composer 加到 project 內。這個過程都算輕鬆,因為這些組件是一般常用的組件,很容易找到。

    Laravel 4 改用 Composer 管理組件的安裝、更新及相依性

    Laravel 3 和 Laravel 4 的用法上的改動可以參考 Jeffery Way 的 Laravel 4 Mastery 和 Jason Lewis 的 Laravel 4: Illuminating Your Laravel 3 Applications。不過因為這些影片和文章都是在 Beta 版時寫的,和正式版會有小量出入。以下是我升級 CityU GE 指南時主要遇到的變化:

    HTML、Form、Blade、Asset Management、i18n

    • L3 用來產生 HTML <a> tag 的 HTML::link()HTML::link_to_route()HTML::link_to_secure() 在 L4 變成了 global function link_to()link_to_action()
    • Form class 仍然存在,在 L4 beta 時曾傳出 L4 會取消
    • Form::open() 的傳入參數改為一個 associative array
    • Asset class 已經消失,要自己找替代品,或者乾脆不用
    • Blade 的 @include() 易名為 @extends()
    • Blade 的 @endsection 易名為 @stop(但不轉也可以用到,但上面的 @include 就一定要改)
    • Blade 新增了{{{ }}} 語法,括在裏面的 variable 會做一次 HTML escape;而原有的 {{ }} 語法就和以前一樣不會 escape
    • e() 這個 escape HTML entity 的 function 仍然存在
    • Lang class 已經可以支援單數 / 眾數 / 個別數量時會套用不同款的翻譯字串
    • L3 內 __() 這個模仿 gettext 的 functoin 在 L4 改為 trans()trans_choice() 或者 Lang::get()Lang::choice()

    Pagination

    • Paginate 過的 variable 如果要取得本頁的行列的話 L3 是 foreach ($courses->results as $course) {...};L4 則不用再加 results,即是 foreach ($courses as $course) {...}
    • 因為上述的改動,如果要檢查 paginate 過的結果是否為空的話,要使用 if (!$courses->getTotal()) {...}

    Route

    • 新增了 resource controller:Route::resource()(可以方便做 RESTful API,它會自動產生 RESTful URL,並且會有 route name)
    • 新增了 sub-domain routing
    • 新增了 route prefix(即是網址中段的 directory 名),例如要做一個 admin section,不用再每個 admin section 的 route 加 /admin/,可以用 Route::group(array('prefix' => 'admin'), function() {...}) 包起所有 admin section 的 route

    URL、Request、Response

    • URL::base() 這個 function 改為使用 URL::to('')
    • L3 要取得目前這個 request 的 route name 是用 Request::route()->action['as'] 而 L4 是用 Route::currentRouteName()
    • 要送出 HTTP 404、500 等 error,L3 是用 return Response::error('404') 而 L4 是用 return App::abort(404)

    Raw Query、Eloquent、Migration

    • L3 raw query 的 DB::only() 在 L4 已經取消
    • L4 可以用到 transaction
    • L3 要執行 raw query 是 DB::query() 而 L4 則分拆成 DB::select()DB::insert()DB::update()DB::delete()DB::statement
    • Eloquent model 的 getter 和 setter function 的命名規則分別改為 getFooAttribute()setFirstNameAttribute()FooFirstName 是 database column 名(留意 column 名仍然是用 snake_case,所以 setFirstNameAttribute() 是對應 database table 的 first_name column)
    • 新加了 database seed 功能,可以不用再將填測試或預設資料寫成 DB schema 的 migration,並且在有需要時才導入到資料庫中
    • 經 Eloquent 取得的 created_atupdated_atdeleted_at column 不再是普通的 string,而是 extends 自 DateTimeCarbon object,它提供一些方便易用的 function 來切換顯示格式、運算和處理這些日期和時間

    Artisan

    • 新增了 php artisan serve,只要讓 console 開着的話就可以用 PHP 內置的網頁伺服器運行這個 Laravel project,可以不用設定 VirtualHost
    • 新增了 php artisan controller:make,用來產生 resource controller
    • 新增了 php artisan optimize,用來產生最佳化過的 class loader,加快載入速度
    • 新增了 php artisan migrate:refresh,可以還原所有 migration 並重新執行所有 migration

    其他

    • dd() 仍然可以使用
    • L4 目前未有內置 profiler bar (Anbu),但可以用 loic-sharma/profiler 代替 其實還有很多的改變,只是我在這次升級未有遇到。要查 L4 的說明文件可以到 Laravel 官方網站,官方網站亦都有 L4 API。說明文件並沒有將所有 function 列出,如果要找一些比較少用的 function 就要到 API documentation 查。如果已經有 L3 的 project 的話,其實可以繼續用 L3。因為升級不只是換 Laravel 的核心檔案,還要逐個檔案去改。但如果是要開始一個新 project 的話,就最好用 L4。因為 L3 將會淡出,日後未必再有新的 L3 bundle 可以用。相反,L4 是用 Packagist,愈來愈多 PHP framework 都開始支援 Packagist / Composer,這意味着日後會有更多現成組件可以用,節省自己開發的時間。
    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2013/06/prepros/index.html b/2013/06/prepros/index.html index d8a663ce..2bb44c5f 100644 --- a/2013/06/prepros/index.html +++ b/2013/06/prepros/index.html @@ -1,7 +1,7 @@ 萬用 Preprocessor Compiler:Prepros | EricLog -

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!

Prepros 的界面簡潔,布局還有幾分像 CodeKit

我自己只是 CSS 才用 preprocessor,因為寫傳統的 CSS 如果 style 一多檔案就會好長,而且那些 CSS selector 不時都會重複使用,令到 CSS 變得很累贅。用了 Preprocessor 之後因為它們有 Variable、Mixin、Nesting 等等的功能,令到寫 CSS 有寫 program 的感覺。它令到寫 CSS 和日後修改設計變得更方便,例如將網頁的主色都寫到 variable,日後改顏色的話就只需要改 variable。Mixin 即是類似 programming 的 function,可以有 parameter。你可以將那些 CSS3 的新 properties 寫入一個 mixin,只要一調用這個 mixin 就會為你加上 W3C、WebKit、Mozilla、IE 對應的 properties,這樣就不需要每次都要同時寫幾個 vendor-specific properties。(其實 vendor-specific properties 的 mixin 是不用自己寫的,因為已經有現成。Sass / Scss 有 Compass;LESS 有 LESS Hat)如果是做 Responsive Layout 的話,一般的 CSS Preprocessor 可以讓你將 @media 寫到 selector 的 { } 入面(稱為 @media bubbling),這樣可以將相關的東西集中起來,方便修改,不用再自己另外找個位放 @media 的部分。要解決 CSS 檔過長的問題,可以將不同的部分儲存成一個個 LESS / Sass / Scss 等 Preprocessor 檔案。之後再開一個 Preprocessor 檔案將其餘的檔案用 @import 來加入到這一個檔案中。最後用 compiler 將這一個檔案轉成 CSS。這樣做就可以生成到一個包含了所有 CSS Rule 的檔案,而 Twitter Bootstrap 也是用這個方法。

我自己試過用 Scss + Compass 和 LESS + Twitter Bootstrap,兩者其實都是差不多。初次使用 CSS Preprocessor 可以考慮使用 Scss 和 LESS,因為它們都是直接衍生自傳統 CSS,syntax 和傳統 CSS 最相近。一個正確的傳統 CSS 檔案本身已經是一個正確的 Scss / LESS 檔案。如果想了解 Sass、Scss 和 Compass 的話,可以看看 Brandon Mathis 的 Sass & Compass: The future of stylesheets now。用過 CSS Preprocessor 之後保證愛不釋手,不會再想寫傳統的 CSS 了!

comments powered by Disqus
\ No newline at end of file diff --git a/2013/07/reparing-xperia-ion/index.html b/2013/07/reparing-xperia-ion/index.html index 44af36ff..54142fdc 100644 --- a/2013/07/reparing-xperia-ion/index.html +++ b/2013/07/reparing-xperia-ion/index.html @@ -4,19 +4,15 @@ 鏡頭鍍膜被刮傷,導致照片影像矇矓 -">

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。

鏡頭鍍膜被刮傷,導致照片影像矇矓
鏡頭鍍膜刮傷後所拍的照片,即使相機的焦距已設為無限遠,但都不能對焦

手機買了差不多一年都沒有留意鏡頭原來是有鍍膜的。馬上記起讀 A-Level Physics 學過的 Thin film,有鍍膜的鏡片會令反射的光線出現 π phrase change 然後有 destructive interference,令照片的色彩更佳。難怪 Xperia ion 主打功能是相機。如果鍍膜表面刮花的話,折射過的光線就會在 CCD/CMOS 表面散開,導致影像模糊。

台灣的 Mobile01 討論區有人查詢過 Xperia ion 鏡頭鍍膜花了的話保修替換要付多少錢,有人回覆說可以用玻璃清潔劑清除鍍膜,影像就會回復清晰。我自己試做過,清除了大部分的鍍膜之後的確馬上「目復光明」,不過白平衡可能會比起有鍍膜時有小許偏差。不過要細心留意才會發現得到。

用清潔劑去除了大部分鍍膜

雖然拍照功能回復正常,但聽 Kevin 說他的 Xperia ion 之前又是鍍膜刮傷,拿去 Sony 維修是可以免費換鏡片。趁着保養期快要到期,於事我又拿去旺角維修中心換,不過職員說要三日後才能拿機,叫我早上去,那樣會比較高機會能即日拿回手機。幾日後的早上(十一點幾)我到旺角維修中心向職員說拍照有時會不清楚,而且白平衡有時會有問題(因為鍍膜刮傷是人為問題,應該不可能免費更換),職員說三個小時後就能拿回手機。

三個小時後到中心取機,他們真是會免費換鏡片!可能因為我的刮痕未穿過鍍膜所以不算作人為損壞。不過日後再有刮痕的話就只可以人手清除鍍膜。如果大家是用 Sony 的手機的話要留意一下鏡頭有沒有鍍膜,有的話就要小心不要將手機放近鎖匙之類的尖物,以免刮到鏡頭鍍膜。下圖就是換了鏡片後所拍的一張相:

更換鏡頭後所拍的照片,相片回復清晰
comments powered by Disqus
\ No newline at end of file diff --git a/2013/12/conemu/index.html b/2013/12/conemu/index.html index 760f4060..be275c3d 100644 --- a/2013/12/conemu/index.html +++ b/2013/12/conemu/index.html @@ -1,7 +1,7 @@ ConEmu | EricLog -

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有分頁功能的 console,和 Mac 的 iTerm 有點相似。它的特色就是可以讓你自由地設定各項細節。由字型、字體大小到啟動時開啟那一個目錄都能設定到。它除了可以調用 Windows 的 Command Prompt (cmd.exe) 之外,還可以調用 PowerShell、Bash 等等的 shell。如果用 Bash 的話可以用 Git 提供的版本,這個版本可以支援基本的 Bash 指令,用來執行 PHP CLI 程式、Grunt、PHPUnit 都有顏色顯示。

在 ConEmu 內使用 Git Bash

ConEmu 的基本設定可以參考 scotch.io《Get a Functional and Sleek Console in Windows》,內有介紹如何設定一個使用 Git Bash 的 Task(即是設定檔之類的東西)和介面。

comments powered by Disqus
\ No newline at end of file diff --git a/2014/03/android-studio-0-5-2-actionbarcompat-crash-app-on-android-2-3/index.html b/2014/03/android-studio-0-5-2-actionbarcompat-crash-app-on-android-2-3/index.html index 4ed8993a..af5b9d63 100644 --- a/2014/03/android-studio-0-5-2-actionbarcompat-crash-app-on-android-2-3/index.html +++ b/2014/03/android-studio-0-5-2-actionbarcompat-crash-app-on-android-2-3/index.html @@ -1,8 +1,8 @@ Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app | EricLog

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$$MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。

另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。

第一個方法是修改 project 目錄的 build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
+另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。">

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$$MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。

另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。

第一個方法是修改 project 目錄的 build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
 
 buildscript {
     repositories {
@@ -51,8 +51,8 @@
     compile 'com.android.support:appcompat-v7:19.0.1'
     compile 'com.google.android.gms:play-services:4.2.42'
 }

改完之後按一下 Sync Project with Gradle Files,之後 build 出來的 app 就正常了。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2014/05/2015-hk-general-holidays-calendar/index.html b/2014/05/2015-hk-general-holidays-calendar/index.html index 47d529fd..9f46b8bf 100644 --- a/2014/05/2015-hk-general-holidays-calendar/index.html +++ b/2014/05/2015-hk-general-holidays-calendar/index.html @@ -1,7 +1,7 @@ 更新香港公眾假期日曆 | EricLog -

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

comments powered by Disqus
\ No newline at end of file diff --git a/2014/08/devdocs/index.html b/2014/08/devdocs/index.html index ed5efb25..c01b54e4 100644 --- a/2014/08/devdocs/index.html +++ b/2014/08/devdocs/index.html @@ -1,7 +1,10 @@ DevDocs | EricLog -

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是免費的。

但如果不想安裝軟件的話,可以用 DevDocs。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。

如果找不到所需的文件,還可以到他們的 Trello 提議和投票。

comments powered by Disqus
\ No newline at end of file diff --git a/2014/09/data-one-traffic-speed-map/index.html b/2014/09/data-one-traffic-speed-map/index.html index 4b5310d2..7d5bdc4a 100644 --- a/2014/09/data-one-traffic-speed-map/index.html +++ b/2014/09/data-one-traffic-speed-map/index.html @@ -1,7 +1,7 @@ Data.One 的行車速度圖 | EricLog -

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。

我已經將行車速度圖 Excel 內的點轉成 WGS84 座標,而且做了個 KML 檔案標示各點和線。KML 檔可以用 Google Earth 開啟。(KML 內的物件已超出 Google Maps My Place 匯入 KML 檔的上限,所以要用 Google Earth 開啟。)

行車速度圖的路段
comments powered by Disqus
\ No newline at end of file diff --git a/2014/09/thinkpad-dead-battery/index.html b/2014/09/thinkpad-dead-battery/index.html index 1f97b26f..2886b378 100644 --- a/2014/09/thinkpad-dead-battery/index.html +++ b/2014/09/thinkpad-dead-battery/index.html @@ -1,7 +1,7 @@ ThinkPad 死電池 | EricLog -

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。

新電池在用了幾個月後就錯估電量。當電量剩餘約三分之一時,Windows 和 ThinkVantage Power Manager 就會突然提示電量只有 6%。如果不在幾分鐘內插上電源電腦就會自動關機。試過校正電池電量,但過了一段時間又會打回原形,於事又要到維修中心一趟。這次因為電池保養未完,所以直接換了電池。新電池大約可以用到三個多小時,不知這次壽命如何。三粿電池的芯都是用 SANYO,但之前兩粿的電池品質都有問題。

除了主電池之外,底板的電池用了一年多就沒電。一般桌上電腦的底板電池用了五六年才會用完,底板電池用了一年多就用完簡直離譜。另外,它的 Windows recovery media 也是有問題的。買機後馬上用 USB 手指做了一個 recovery media,後來用這個 USB 手指做系統還原時發現有錯誤,結果要拿去維修中心重裝系統。

ThinkPad 的品質差原來都不是新鮮事,原來前幾年發生過 ThinkPad E420「主板門」事件。雖然 E 系不是原 IBM 系列,但都可以見到 Lenovo 的品質問題不是個別事件。日後再買電腦都應該不會再考慮 Lenovo。

comments powered by Disqus
\ No newline at end of file diff --git a/2015/01/oneplus-one-xperia-z2/index.html b/2015/01/oneplus-one-xperia-z2/index.html index cebce92b..4e826a72 100644 --- a/2015/01/oneplus-one-xperia-z2/index.html +++ b/2015/01/oneplus-one-xperia-z2/index.html @@ -1,10 +1,10 @@ OnePlus One + Xperia Z2 | EricLog

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。

之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。

OnePlus One

首先,OnePlus One 的背殼是它一大特色。Sandstone Black 的質感像砂紙、指甲銼,但不會太刮手。好處是防滑,相信不會像皮革漆般幾年後變質,變得黏答答的(之前的 Motorola Milestone 機背的皮革漆已經變質)。

第二個特色就是用了基於 Android 4.4 而客制的 CyanogenMod 11S。基本上和其他手機的 CyanogenMod ROM 差不多,但會有專為 OnePlus One 訂造的相機 App。好處就是沒有多餘的預載 App,而且更新出得頻密。不用太擔心會很快變孤兒機。還有大量細節可以逐一設定。不過如果新接觸的話可能會覺得難用,因為實在有太多設定。

不過試了一日就決定退貨。因為 5.5" 屏幕實在太大部,而且香港的售後服務幾乎等於沒有。而且震機太弱(只聽到摩打聲但感受到震動不強)。如果開了靜音再放入褲袋的話幾乎不會留意到電話震機。不過都可以看到 OnePlus 在推出這部手機時都有花心思設計和肯聽用家的意見。例如可以讓用戶選擇用實體按鍵或虛擬按鍵、用了特別設計的扁身 USB 線、同時提供 Micro-SIM 和 Nano-SIM 卡托。雖然他們的口號是 Never Settle,但產品仍非完美。比如沒有英制三腳插、屏幕底部變黃、OS 有 bug 等等。網上有消息指 OnePlus 日後會改用自家制 ROM 而非 Cyanogen 的 ROM。自家制 ROM 在大陸機很常見,經常會作為手機的賣點之一。但如果外銷的話 CyanogenMod 會比較吸引,因為這部手機的用家都是玩家為主。

退貨過程前後大約需要一個月,大部分時間都是在等網上售後的指示。不過最後都可以退到錢(連運費都可以退)。退款會直接退到 PayPal 戶口。

OnePlus One 包裝
OnePlus One 包裝盒
OnePlus One 包裝盒
OnePlus One 正面
OnePlus One 背面
OnePlus One 本體、扁身 USB 線和 SIM 卡槽針
OnePlus One Micro-SIM 及 Nano-SIM 卡托

Sony Xperia Z2

因為 Z3 經已上市,所以買 Z2 只需要 $3700。我買的是白色版,主要原因是因為邊框比較美觀和耐磨。黑色和紫色版的金屬邊是有上漆,而非金屬原色。如果長期使用可能會掉漆。屏幕是 5.2" IPS,解像度都是 1920 × 1080 px,尺寸比 OnePlus One 細小許。但單手使用一樣困難。位置遠的話需要用另一隻手點擊。雖然 Z2 和 OnePlus One 尺寸比 ion 大,但顯示的內容都是一樣,只是將畫面放大和 DPI 加大。相機就有 20.7 MP,比 OnePlus One 高,還可以錄影 4K 影片。但網上的評論話只可以錄幾分鐘,因為很快會過熱。電量方面,ion 大約只可以用半日但 Z2 就可以用全日。

Z2 的造工其實都不算好。首先,正面膠邊和玻璃之間有空隙。這個問題網上都有人報告過,應該是通病。另一個問題是後置鏡頭安裝位置不夠正中。除此之外,前後玻璃面是用塑膠包邊,但開箱時已經發現有輕微污跡。機身四邊除了用塑膠之外,還用了金屬框做裝飾。但削邊不夠直,令開蓋和邊框線條不連接。而白色的膠框在開箱時已經看到有小許灰色污漬。如果長期使用的話可能會變色。

音效方面,Z2 的啦叭放在正面,不像 ion 放到背面,所以聲音不易被擋住。而 Z2 亦沒有 ion 拆聲的問題。而買 Z2 還送了一個 MDR-NC31EM 降噪耳機。這個降噪耳機的特色就是不需要用電池,因為降噪功能是交由手機去做。這個耳機基本上就只是一個雙聲道入耳耳機連雙聲道收音咪。降噪的效果不俗,在西鐵上使用時行車和冷氣的聲音還會聽到,但音量已經降低。但人聲例如廣播、電視的聲音就沒有特別減弱。這個耳機雖然可以用來打電話,但就沒有線控和線夾,跟機的耳機就有線控但沒有線夾。反而 ion 跟機的耳機就有齊兩樣。

Xperia Z2 正面
Xperia Z2 背面
側面用銀色金屬和白色塑膠,但金屬削邊不均勻
背面鏡頭不是置中
正面玻璃和塑膠邊框部分角位留有較大空隙
附送的 MDR-NC31EM 降噪耳機
Xperia Z2 的用電情況

comments powered by Disqus
\ No newline at end of file diff --git a/2015/03/inline-css/index.html b/2015/03/inline-css/index.html index 77d5b666..73f257be 100644 --- a/2015/03/inline-css/index.html +++ b/2015/03/inline-css/index.html @@ -5,19 +5,14 @@ 排版要用 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 -">

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如:

  • CSS 要 inline,<style> 並不是全部 email client 都能支援
  • 排版要用 <table>
  • 圖片視乎需要當成附件,Base64 未必能被 email client 支援 +">

    Inline CSS

    最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如:

    • CSS 要 inline,<style> 並不是全部 email client 都能支援
    • 排版要用 <table>
    • 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulpgulp-inline-cssgulp-inline,效果不錯。

    以下是 Gulp 做 inline CSS 的寫法:

    var gulp = require('gulp');
     var minifyHTML = require('gulp-minify-html');
     var inlineCSS = require('gulp-inline-css');
    @@ -37,8 +32,8 @@
             .pipe(minifyHTML())
             .pipe(gulp.dest('templates/email'));
     });

    inline 是用來將 HTML 用 <style> 外連的 CSS 檔變成內篏在 HTML 檔的 CSS。之後 inlineCSS 會將 <style> 的 CSS 變成 inline 在每一個 HTML tag 的 style attritube。最後 minifyHTML 是用來將多餘的空格清走,令檔案變得更細。將它抽走都不會影響 inline CSS 的效果。如果不用 gulp-inline 而直接用 gulp-inline-css 的話會令到原先用 <style> 內篏在 HTML 檔的 CSS 不能 override 外連 CSS 檔的 rule。

    有一樣東西要留意:inlineinlineCSS 會將 HTML 檔內本應要 escape 的字符 escape 掉。所以如果 HTML 檔是要配合 template engine 用的話,要留意 template engine 所用的字符會否被 escape 掉。

    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2015/05/managing-mp3-id3-tag/index.html b/2015/05/managing-mp3-id3-tag/index.html index 65074a85..6758a200 100644 --- a/2015/05/managing-mp3-id3-tag/index.html +++ b/2015/05/managing-mp3-id3-tag/index.html @@ -1,7 +1,10 @@ 整理 MP3 ID3 tag | EricLog -

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。

如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2,而下面就只揀選 ID3v2.3 UTF-16。而在 Remove 部分則剔選 ID3v1 及 APE。這樣就應該不會再有亂碼。

如果要移除其他 tag(例如歌詞、iTunes 遺留下來的 tag),可以在 Mp3tag 的檔案欄的項目按右鍵,然後點選 Extended tags。歌詞的 tag 是 UNSYNCEDLYRICS

iTunes 都可以整理 MP3 的 ID3 tag,但預設是不會將 tag 的改動寫回 MP3 檔內,如果沒有特別處理過的話在其他 player(例如電話)播放就未必見到改動過的 ID3 tag。

如果要補回專輯的 cover art 的話,除了 Google image search 之外,iTunes Store 本身都有提供大量 cover art。除了用 iTunes 來自動補回 cover art 之外,也可以用 iTunes Artwork Finder。只需揀選 Album,輸入專輯名並選擇 iTunes Store 地區就可以開始搜尋。網站會提供 600 px 和 1500 px 的圖像。只需右鍵複製再在 Mp3tag 的 Cover 欄右鍵貼上就能替歌曲補回 cover art。

comments powered by Disqus
\ No newline at end of file diff --git a/2015/08/parse-android-push-notification/index.html b/2015/08/parse-android-push-notification/index.html index 72d410bb..fb13e5fb 100644 --- a/2015/08/parse-android-push-notification/index.html +++ b/2015/08/parse-android-push-notification/index.html @@ -1,5 +1,5 @@ Parse + Android 收 Push Notification | EricLog -

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。

第一步: 在 Android Studio 按照平時開新 project 的方法來建立一個新的 project。

第二步: 在 module 的 build.gradle 加入 Parse 的 dependency。

dependencies {
+

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。

第一步: 在 Android Studio 按照平時開新 project 的方法來建立一個新的 project。

第二步: 在 module 的 build.gradle 加入 Parse 的 dependency。

dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile 'com.android.support:appcompat-v7:23.0.0'
     compile 'com.parse.bolts:bolts-android:1.2.1'
@@ -92,8 +92,8 @@
 
     ParseAnalytics.trackAppOpenedInBackground(getIntent());
 }

第七步: 現在已經完成設定,在設備 run 一次 app,讓它向 Parse 註冊 push notification。然後到 Parse 網站發送一個 push,你的設備應該會收到 push。而且 database 的 Installation class 會有一筆新記錄。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2015/09/delete-long-path-in-windows/index.html b/2015/09/delete-long-path-in-windows/index.html index 0de4532d..d772dc08 100644 --- a/2015/09/delete-long-path-in-windows/index.html +++ b/2015/09/delete-long-path-in-windows/index.html @@ -1,7 +1,7 @@ 在 Windows 刪除路徑名太長的檔案 | EricLog -

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。

但原來解鈴還須繫鈴人,有人做了一個簡單的 Node 工具 rimraf 可以順利地移除這些資料夾。

首先,用 NPM 安裝 rimraf:

npm install -g rimraf

然後,將要刪除的資料夾傳到 rimraf 即可:

rimraf C:\Users\Eric\Documents\proj
comments powered by Disqus
© 2024 EricLog +

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。

但原來解鈴還須繫鈴人,有人做了一個簡單的 Node 工具 rimraf 可以順利地移除這些資料夾。

首先,用 NPM 安裝 rimraf:

npm install -g rimraf

然後,將要刪除的資料夾傳到 rimraf 即可:

rimraf C:\Users\Eric\Documents\proj
comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/03/migrated-to-hexo/index.html b/2016/03/migrated-to-hexo/index.html index 5a8b7f91..2a050db2 100644 --- a/2016/03/migrated-to-hexo/index.html +++ b/2016/03/migrated-to-hexo/index.html @@ -1,7 +1,10 @@ 轉用 Hexo | EricLog -

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。

Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code> 之類的 tag,用了不少時間才寫完。

寫文章方面,因為匯出 HTML 需要先安裝 Hexo,所以不能像 WordPress 般方便。我的做法是在 Cloud9 開一個 workspace 來寫文。這樣就不用每部機都要裝一次 Hexo,而且下一次開啟時還能保留 editor 和 shell 的 context。寫文時亦可以開啟 server 來預覽網站。完成後就可以 deploy 到 GitHub Pages。如果嫌沒有網頁寫作介面的話可以安裝 hexo-admin plugin。

轉移過程最麻煩的地方就是要人手逐篇文章去檢查轉換完的 Markdown。雖然 Hexo 有提供 WordPress 轉移 plugin,但因為它的轉換插件只是作簡單的轉換,沒有特別去 escape Markdown 語法的字符。加上它沒有將上載到 WordPress 的圖片下載一次。所以要逐張圖片人手處理。有些文章實在有太多圖片,我在轉移的時候只保留部分的圖片。留言方面,因為先前已經轉用 Disqus,所以只需要在設定檔填上 Disqus ID 就可以在文章顯示以前的留言。

comments powered by Disqus
© 2024 EricLog +

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。

Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code> 之類的 tag,用了不少時間才寫完。

寫文章方面,因為匯出 HTML 需要先安裝 Hexo,所以不能像 WordPress 般方便。我的做法是在 Cloud9 開一個 workspace 來寫文。這樣就不用每部機都要裝一次 Hexo,而且下一次開啟時還能保留 editor 和 shell 的 context。寫文時亦可以開啟 server 來預覽網站。完成後就可以 deploy 到 GitHub Pages。如果嫌沒有網頁寫作介面的話可以安裝 hexo-admin plugin。

轉移過程最麻煩的地方就是要人手逐篇文章去檢查轉換完的 Markdown。雖然 Hexo 有提供 WordPress 轉移 plugin,但因為它的轉換插件只是作簡單的轉換,沒有特別去 escape Markdown 語法的字符。加上它沒有將上載到 WordPress 的圖片下載一次。所以要逐張圖片人手處理。有些文章實在有太多圖片,我在轉移的時候只保留部分的圖片。留言方面,因為先前已經轉用 Disqus,所以只需要在設定檔填上 Disqus ID 就可以在文章顯示以前的留言。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/07/olympus-em10/index.html b/2016/07/olympus-em10/index.html index cb4f5e5a..392203cb 100644 --- a/2016/07/olympus-em10/index.html +++ b/2016/07/olympus-em10/index.html @@ -1,7 +1,7 @@ Olympus E-M10 | EricLog -

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。

買相機前有留意過 Sony A5100 和 Sony A6000。最初是考慮過 A5100,但發覺機身太少按鈕,轉換設定要多按幾個按鍵會不方便,之後就考慮 A6000。A6000 整體而言很好,只是價錢比較貴。之後才開始考慮 E-M10。那時買 E-M10 除了兩支 kit 鏡(M.ZUIKO DIGITAL ED 14-42mm F3.5-5.6 EZ、M.ZUIKO DIGITAL ED 40-150mm F4.0-5.6 R)之外,還加多支 M.ZUIKO DIGITAL 17mm F1.8 定焦鏡。

我比較喜歡 E-M10 的地方是它有輕觸式屏幕,可以用手指點一下就能指定對焦點,不需要用十字掣。但進入選單後都需要用十字掣操作。還有就是它有對應的 Android 和 iOS app 方便你將相片抄去手機分享和用手機搖控拍照。還有個功能是 Live Composite。簡單來說,它就是替你自動疊相。先拍一張圖做底圖,然後就不斷替你拍照,將新的光部分疊加到底圖上。所以它可以在長曝拍車軌時背景不會過曝,或者可以在日天時拍 light painting。如果不相疊相的話,在使用 T 快門和 B 快門拍攝時都能即時預覽目前拍攝效果,非常方便。

但它不足之處就是沒有五軸機身防震(只有三軸),Fn 按鍵只有兩個不夠用和 noise 多。相片多 noise 是 sensor 細的缺點。ISO 1600 還可以接受,但到 ISO 3200 就有很多 noise。過了 ISO 3200 基本上都不能用,除非相片只會用來上載到 Facebook 分享。所以大光圈鏡頭在 M4/3 是非常有用,因為可以令 ISO 降低。

如果想看看相片的話,可以到我的 Flickr 參觀。下面就是我最近到流浮山影的相片。

流浮山海邊日落 (M.ZUIKO DIGITAL 17mm F1.8, f/7.1, 0.8s, ISO 200)

comments powered by Disqus
© 2024 EricLog +

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。

買相機前有留意過 Sony A5100 和 Sony A6000。最初是考慮過 A5100,但發覺機身太少按鈕,轉換設定要多按幾個按鍵會不方便,之後就考慮 A6000。A6000 整體而言很好,只是價錢比較貴。之後才開始考慮 E-M10。那時買 E-M10 除了兩支 kit 鏡(M.ZUIKO DIGITAL ED 14-42mm F3.5-5.6 EZ、M.ZUIKO DIGITAL ED 40-150mm F4.0-5.6 R)之外,還加多支 M.ZUIKO DIGITAL 17mm F1.8 定焦鏡。

我比較喜歡 E-M10 的地方是它有輕觸式屏幕,可以用手指點一下就能指定對焦點,不需要用十字掣。但進入選單後都需要用十字掣操作。還有就是它有對應的 Android 和 iOS app 方便你將相片抄去手機分享和用手機搖控拍照。還有個功能是 Live Composite。簡單來說,它就是替你自動疊相。先拍一張圖做底圖,然後就不斷替你拍照,將新的光部分疊加到底圖上。所以它可以在長曝拍車軌時背景不會過曝,或者可以在日天時拍 light painting。如果不相疊相的話,在使用 T 快門和 B 快門拍攝時都能即時預覽目前拍攝效果,非常方便。

但它不足之處就是沒有五軸機身防震(只有三軸),Fn 按鍵只有兩個不夠用和 noise 多。相片多 noise 是 sensor 細的缺點。ISO 1600 還可以接受,但到 ISO 3200 就有很多 noise。過了 ISO 3200 基本上都不能用,除非相片只會用來上載到 Facebook 分享。所以大光圈鏡頭在 M4/3 是非常有用,因為可以令 ISO 降低。

如果想看看相片的話,可以到我的 Flickr 參觀。下面就是我最近到流浮山影的相片。

流浮山海邊日落 (M.ZUIKO DIGITAL 17mm F1.8, f/7.1, 0.8s, ISO 200)

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/07/photosphere/index.html b/2016/07/photosphere/index.html index c6f3f0b8..c1096130 100644 --- a/2016/07/photosphere/index.html +++ b/2016/07/photosphere/index.html @@ -1,7 +1,33 @@ Photosphere | EricLog -

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。

以下是所需工具:

  • 有全手動模式的相機
  • 魚眼鏡 / 廣角鏡
  • 三腳架
  • Hugin
  • Photoshop
  • 能修改檔案 XMP 的工具

器材

按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。

緊記要用三腳架以確保位置固定在一個點。如果要講夠的話可以用全景雲台,但就算不用效果也是可以的。拍攝時要確保相片之間要預留足夠的重疊位置。PhotosphereViewer.net 提供了一個建議可以作參考:相機垂直,分 7 行影,水平角度影 14 張,之後慢慢將相機角度調向天上分別影 12 張、6 張、1 張,然後向地面做相同動作。不過拍到底部時要拿走腳架才拍。

後製

Hugin 是一個開源的全景圖接駁軟件,它能支援不同的投射方式 (projection)。它支援 Photosphere 的 2:1 equirectangular projection。

Panorama Stitcher

開啟 Hugin 之後,可以先加入水平角度影的一行相片讓它自動接駁。用水平角度影的一行應該是最易接駁。留意要設定好鏡頭的焦距,因為它會按照焦距來將相片投射到球體內。如果相片見到腳架的話,可以Hugin 內置了不同的算法來尋找兩張圖的共通點,將相片加到 project 之後應該先用那些算法自動做一次先(揀選相片,然後按 Create control points 按鈕),如果效果不好才自己手動逐個共通點標註。留意揀選相片再按 Create control points 按鈕這個步驟要以人手揀選不同的相片組合再按 Create control points 按鈕。因為它有時不會尋找跨行相片的共通點。駁了大約一行後可以按一下 Optimize 部分的 calculate 按鈕,讓它計算相片的位置。

用 Mask 將腳架減去

這個步驟的目標就是要「織網」:即是將不同的相片以共通點連接,連接不單止是同一行,還要跨行。否則相片不能接駁成球體。要查看「織網」的效果,按一下 Fast panorama preview 按鈕(頂部寫有 GL 的按鈕),再轉到 Layout tab 就可以看到。如果「織網」效果不滿意,可以返回 Panorama Stitcher 視窗再揀選未有連結的兩張圖,再按一下 Create control points 按鈕。如果一次過揀選太多圖片來找共通點的話可能「織網」時會有遺漏。

Fast panorama preview - layout

之後可以轉到 Preview tab 預覧效果。如果滿意的話可以將圖片匯出。留意匯出的圖片太大的話可能會因記憶體不足而失敗。匯出完成後可以用 PhotosphereViewer.net 預覽。

Preview panorama

預覽時可能會發現頂部和底部都不能完全填滿﹑還見到小小腳架,或者端點駁位特別明顯,但用 Photoshop 修補又很難。Equirectangular projection 的特點就是頂部 (zenith) 和底部 (nadir) 會嚴重變形,如果直接修改的話會非常困難。所以這時要用 The Domemaster Photoshop Actions Pack Photoshop action 將頂部和底部還原。

裝好 The Domemaster Photoshop Actions Pack 之後,開啟之前匯出好的全景相,然後複製多一個文件出來 (Image | Duplicate)。如果要修改底部的話,就要先將圖片 180 度反轉。之後在 Action 面板選擇「2:1 Equirectangular to Angular Fisheye」。還原後會變成一個圓形的圖片,這時就可以用平時慣用的工具(例如 Clone stamp tool)來修補。如果懶的話可以放一個圓形的圖案遮蓋住底部,圖案可以是你的 logo 或者指南針之類。改好之後就合併所有圖層,之後在 Action 面板選擇「Angular Fisheye to 2:1 Equirectangular」。圖片就會變成 equirectangular projection,這就可以將該圖片放回原先的全景圖內。留意相片尺寸不可以太大,否則 Photoshop 不能處理。還有做完 Angular Fisheye to 2:1 Equirectangular Action 後圖片會被拉高,只需在放上去原圖時將高度縮小一半就可以了。

The Domemaster Photoshop Actions Pack

最後就是為全景圖加上 Photosphere XMP metadata。我自己就用 Adobe Bridge 的面板加上 data panel 來加工,但其實可以用其他改 EXIF 和 XMP 軟件修改。除了 Photosphere XMP metadata 外,還可以加上相片拍攝位置的座標,方便分享到 Google Maps、Facebook、Flickr 等等的網站。Photosphere XMP metadata 有一項叫 PoseHeadingDegrees 是用來指明北面是在那方,但在 Facebook 試過發現它好像沒有用到這個數值。所以最好的方法都是在 Hugin 裏調節好方向。只需在 Hugin 的 Fast panorama preview 視窗按「Move/Drag」tab,然後按住 Shift 的同時左右 drag 全景圖就可以調整方向,將北面的景放到整張全景點的正中央就可以了。下面就是完成品(因為我之前已經上載了,所以沒有再修正方向問題):

其實可以見到 Hugin 會駁錯,可能是因為用了太多相片去駁,加上可能 Hugin 算法找錯共通點(大廈外牆其實層層都差不多,差異主要是在窗門曬的衣服和冷氣機;地磚就更難駁)。如果要用人手修正 / 補加共通點會花太多時間。如果想大量拍攝 Photosphere 的話最好都是買一支魚眼鏡。

參考

comments powered by Disqus
© 2024 EricLog +

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。

以下是所需工具:

  • 有全手動模式的相機
  • 魚眼鏡 / 廣角鏡
  • 三腳架
  • Hugin
  • Photoshop
  • 能修改檔案 XMP 的工具

器材

按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。

緊記要用三腳架以確保位置固定在一個點。如果要講夠的話可以用全景雲台,但就算不用效果也是可以的。拍攝時要確保相片之間要預留足夠的重疊位置。PhotosphereViewer.net 提供了一個建議可以作參考:相機垂直,分 7 行影,水平角度影 14 張,之後慢慢將相機角度調向天上分別影 12 張、6 張、1 張,然後向地面做相同動作。不過拍到底部時要拿走腳架才拍。

後製

Hugin 是一個開源的全景圖接駁軟件,它能支援不同的投射方式 (projection)。它支援 Photosphere 的 2:1 equirectangular projection。

Panorama Stitcher

開啟 Hugin 之後,可以先加入水平角度影的一行相片讓它自動接駁。用水平角度影的一行應該是最易接駁。留意要設定好鏡頭的焦距,因為它會按照焦距來將相片投射到球體內。如果相片見到腳架的話,可以Hugin 內置了不同的算法來尋找兩張圖的共通點,將相片加到 project 之後應該先用那些算法自動做一次先(揀選相片,然後按 Create control points 按鈕),如果效果不好才自己手動逐個共通點標註。留意揀選相片再按 Create control points 按鈕這個步驟要以人手揀選不同的相片組合再按 Create control points 按鈕。因為它有時不會尋找跨行相片的共通點。駁了大約一行後可以按一下 Optimize 部分的 calculate 按鈕,讓它計算相片的位置。

用 Mask 將腳架減去

這個步驟的目標就是要「織網」:即是將不同的相片以共通點連接,連接不單止是同一行,還要跨行。否則相片不能接駁成球體。要查看「織網」的效果,按一下 Fast panorama preview 按鈕(頂部寫有 GL 的按鈕),再轉到 Layout tab 就可以看到。如果「織網」效果不滿意,可以返回 Panorama Stitcher 視窗再揀選未有連結的兩張圖,再按一下 Create control points 按鈕。如果一次過揀選太多圖片來找共通點的話可能「織網」時會有遺漏。

Fast panorama preview - layout

之後可以轉到 Preview tab 預覧效果。如果滿意的話可以將圖片匯出。留意匯出的圖片太大的話可能會因記憶體不足而失敗。匯出完成後可以用 PhotosphereViewer.net 預覽。

Preview panorama

預覽時可能會發現頂部和底部都不能完全填滿﹑還見到小小腳架,或者端點駁位特別明顯,但用 Photoshop 修補又很難。Equirectangular projection 的特點就是頂部 (zenith) 和底部 (nadir) 會嚴重變形,如果直接修改的話會非常困難。所以這時要用 The Domemaster Photoshop Actions Pack Photoshop action 將頂部和底部還原。

裝好 The Domemaster Photoshop Actions Pack 之後,開啟之前匯出好的全景相,然後複製多一個文件出來 (Image | Duplicate)。如果要修改底部的話,就要先將圖片 180 度反轉。之後在 Action 面板選擇「2:1 Equirectangular to Angular Fisheye」。還原後會變成一個圓形的圖片,這時就可以用平時慣用的工具(例如 Clone stamp tool)來修補。如果懶的話可以放一個圓形的圖案遮蓋住底部,圖案可以是你的 logo 或者指南針之類。改好之後就合併所有圖層,之後在 Action 面板選擇「Angular Fisheye to 2:1 Equirectangular」。圖片就會變成 equirectangular projection,這就可以將該圖片放回原先的全景圖內。留意相片尺寸不可以太大,否則 Photoshop 不能處理。還有做完 Angular Fisheye to 2:1 Equirectangular Action 後圖片會被拉高,只需在放上去原圖時將高度縮小一半就可以了。

The Domemaster Photoshop Actions Pack

最後就是為全景圖加上 Photosphere XMP metadata。我自己就用 Adobe Bridge 的面板加上 data panel 來加工,但其實可以用其他改 EXIF 和 XMP 軟件修改。除了 Photosphere XMP metadata 外,還可以加上相片拍攝位置的座標,方便分享到 Google Maps、Facebook、Flickr 等等的網站。Photosphere XMP metadata 有一項叫 PoseHeadingDegrees 是用來指明北面是在那方,但在 Facebook 試過發現它好像沒有用到這個數值。所以最好的方法都是在 Hugin 裏調節好方向。只需在 Hugin 的 Fast panorama preview 視窗按「Move/Drag」tab,然後按住 Shift 的同時左右 drag 全景圖就可以調整方向,將北面的景放到整張全景點的正中央就可以了。下面就是完成品(因為我之前已經上載了,所以沒有再修正方向問題):

其實可以見到 Hugin 會駁錯,可能是因為用了太多相片去駁,加上可能 Hugin 算法找錯共通點(大廈外牆其實層層都差不多,差異主要是在窗門曬的衣服和冷氣機;地磚就更難駁)。如果要用人手修正 / 補加共通點會花太多時間。如果想大量拍攝 Photosphere 的話最好都是買一支魚眼鏡。

參考

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/08/android-app-git-commit-hash/index.html b/2016/08/android-app-git-commit-hash/index.html index 257b917e..5d5ac8a1 100644 --- a/2016/08/android-app-git-commit-hash/index.html +++ b/2016/08/android-app-git-commit-hash/index.html @@ -1,5 +1,5 @@ 在 Android app 內顯示 Git commit hash | EricLog -

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 appbuild.gradle 就可以了。

import java.util.regex.Pattern
+

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 appbuild.gradle 就可以了。

import java.util.regex.Pattern
 
 apply plugin: 'com.android.application'
 
@@ -53,8 +53,8 @@
         }
     }
 }

上面的 getGitHashVersionName function 就是用 git getGitHashVersionNamegit diff-index 取得 commit hash 和未 commit 的檔案數目。如果有未 commit 的檔案的話,commit hash 後面就會補上檔案數目,方便分辨。

buildTypes 內的 buildConfigField 就是為 Gradle 每次 build 都會自動產生的 BuildConfig class 加入一個 public static final 的 field。只需要在 app 的 source code 直接用 BuildConfig.GIT_VERSION_NAME 就能拿到 Git commit hash。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2016/09/cocoapods-framework-pod/index.html b/2016/09/cocoapods-framework-pod/index.html index bd6d5b24..45672292 100644 --- a/2016/09/cocoapods-framework-pod/index.html +++ b/2016/09/cocoapods-framework-pod/index.html @@ -1,5 +1,5 @@ 自己造一個 CocoaPods Framework Pod | EricLog -

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。

CocoaPods 是管理 Cocoa project 的 dependency manager。其他同類的還有 Carthage,但目前 CocoaPods 還是最多人用。假如我們想做一個叫 MyOwnPod 的私人 CocoaPods close source framework,要準備三個 Git repository:

  1. 用來存放 framework 源碼 (myownpod) +

    自己造一個 CocoaPods Framework Pod

    最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。

    CocoaPods 是管理 Cocoa project 的 dependency manager。其他同類的還有 Carthage,但目前 CocoaPods 還是最多人用。假如我們想做一個叫 MyOwnPod 的私人 CocoaPods close source framework,要準備三個 Git repository:

    1. 用來存放 framework 源碼 (myownpod) 只讓內部組員存取
    2. 用來存放已 compile 的 CocoaPods Pod (myownpod-release) 讓用家存取
    3. 作為 Specs Repository (specs-repo) 讓用家存取,非必須

    我就將步驟以這三個 repository 分成三部分寫。

    Framework 源碼 (myownpod)

    要造一個 CocoaPods 的 pod(即是 dependency)並不需要開一個 Xcode framework project,只需要準備好 source code 和 podspec 檔案就可以了。因為 podspec 內會定義版本、source code 位置、public header file、要調用那些 framework 等等本來是在 Xcode framework project 定義的設定。如果你的 pod 需要依賴其他 pod 的話,也是定義在 podspec 內而不是用 podfile 來定義。

    一個 framework 通常都會有一個示範用的 Xcode project 來讓其他人知道 framework 的用法,而且在開發 framework 時還可以借它來測試一下。不過是 close source 的話這個示範 Xcode project 應該要另外做多一個放到其他地方。因為這個 repository 是不會讓其他人看到的。

    整個 repository 的檔案結構大概概會是這樣:

    .
    @@ -131,8 +131,8 @@
         pod 'OtherThirdPartyPod'
         pod 'MyOwnPod', '~> 0.1.0'
     end

    執行一次 pod install 就會下載剛才自製的 pod。

    補充

    • 如果是做 open source pod 的話其實只需要做第一部分,然後直接交上去官方 repository 就完成。
    • 其實 CocoaPods 有 pod lib create 指令來幫你起步,可以直接用它。
    • CocoaPods 建議用 Git repository 用 HTTPS 而非 SSH 連接,但私人使用的話其實也沒有甚麼問題。
    • CocoaPods Packager 在 build 的時候應該會同時 build 不同的 architecture(即是 universal framework),如果是 open source pod 的話就不用擔心 architecture 問題,因為用家可以自己控制用甚麼 architecture。
    comments powered by Disqus
    comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2016/09/xunique/index.html b/2016/09/xunique/index.html index d8f99cef..be8132fc 100644 --- a/2016/09/xunique/index.html +++ b/2016/09/xunique/index.html @@ -1,7 +1,7 @@ xUnique | EricLog -

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。

而解決 project.pbxproj 容易出現 VCS conflict 的方法就是用 xUnique。它是一個 Python 寫的 script,它會將 project.pbxproj 的所有項目的 UUID 都改用 MD5 hash;而項目都會以英文字母順序排列好。這會確保 UUID 的產生方式和檔案排列次序一致,不會再有個人色彩。這樣就能減低 merge conflict 的機會。

留意 xUnique 要全部組員一齊用才會有效。使用方法可以將它設成 Git 的 pre-commit hook 又或者設定在 Xcode build 時執行。xUnique 暫時發現的缺點就是在執行完 xUnique 後如果令 project.pbxproj 有改動的話 Xcode file tree 的資料夾都會被摺疊,之後要再人手展開才可以回復先前的狀態。

註:在寫這篇文時看到解決 storyboard merge conflict 可以用 StoryboardMerge

comments powered by Disqus
© 2024 EricLog +

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。

而解決 project.pbxproj 容易出現 VCS conflict 的方法就是用 xUnique。它是一個 Python 寫的 script,它會將 project.pbxproj 的所有項目的 UUID 都改用 MD5 hash;而項目都會以英文字母順序排列好。這會確保 UUID 的產生方式和檔案排列次序一致,不會再有個人色彩。這樣就能減低 merge conflict 的機會。

留意 xUnique 要全部組員一齊用才會有效。使用方法可以將它設成 Git 的 pre-commit hook 又或者設定在 Xcode build 時執行。xUnique 暫時發現的缺點就是在執行完 xUnique 後如果令 project.pbxproj 有改動的話 Xcode file tree 的資料夾都會被摺疊,之後要再人手展開才可以回復先前的狀態。

註:在寫這篇文時看到解決 storyboard merge conflict 可以用 StoryboardMerge

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/10/android-pay/index.html b/2016/10/android-pay/index.html index 738de206..c2fe72c4 100644 --- a/2016/10/android-pay/index.html +++ b/2016/10/android-pay/index.html @@ -1,10 +1,10 @@ Android Pay 登陸香港 | EricLog

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。

除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。

而我昨日在百佳試用 Android Pay 付款,過程尚算順利。首先我用 Android Pay 展示易賞錢條碼,收銀員以為我想用 Apple Pay。收銀員先用枱面的條碼機,但不成功。之後再出動手提條碼機,但亦不成功。最後要用鍵盤輸入會員卡號碼。之後到付款部分,過程就非常流暢。只需放上已解鎖並已開啟 NFC 的電話到拍卡機上面就馬上出現付款完成畫面。如果要使用非預設的信用卡,聽講要先在 Android Pay app 內點選信用卡進入詳細資料頁,然後才將電話放近拍卡機。

期望 Android Pay 會支援中銀,還有可以設成每次都可以輸入密碼、指紋才可以交易。更好的話可以支援與記帳 app 同步交易紀錄。

Android Pay app

comments powered by Disqus
© 2024 EricLog +除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。">

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。

除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。

而我昨日在百佳試用 Android Pay 付款,過程尚算順利。首先我用 Android Pay 展示易賞錢條碼,收銀員以為我想用 Apple Pay。收銀員先用枱面的條碼機,但不成功。之後再出動手提條碼機,但亦不成功。最後要用鍵盤輸入會員卡號碼。之後到付款部分,過程就非常流暢。只需放上已解鎖並已開啟 NFC 的電話到拍卡機上面就馬上出現付款完成畫面。如果要使用非預設的信用卡,聽講要先在 Android Pay app 內點選信用卡進入詳細資料頁,然後才將電話放近拍卡機。

期望 Android Pay 會支援中銀,還有可以設成每次都可以輸入密碼、指紋才可以交易。更好的話可以支援與記帳 app 同步交易紀錄。

Android Pay app

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/12/nodetree-jazzy/index.html b/2016/12/nodetree-jazzy/index.html index 424faa92..d0bc129c 100644 --- a/2016/12/nodetree-jazzy/index.html +++ b/2016/12/nodetree-jazzy/index.html @@ -1,7 +1,10 @@ nodetree 和 jazzy | EricLog -

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetreejazzy

nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 nodetree 指令就能印出目前目錄的結構。

而 jazzy 就是一個 Swift 和 Objective-C 的文檔生成器。它和 Java 的 JavaDoc 功能相近,都是按照源碼檔案中以特定格式輸入的註解來生成 HTML 網頁。如果是 Objective-C 的話,註解形式和 JavaDoc 相近。而生成出來的 HTML 網頁外觀和 Apple 官方的 API documentation 非常相似。如果不喜歡的話還可以自訂範本。

comments powered by Disqus
© 2024 EricLog +

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetreejazzy

nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 nodetree 指令就能印出目前目錄的結構。

而 jazzy 就是一個 Swift 和 Objective-C 的文檔生成器。它和 Java 的 JavaDoc 功能相近,都是按照源碼檔案中以特定格式輸入的註解來生成 HTML 網頁。如果是 Objective-C 的話,註解形式和 JavaDoc 相近。而生成出來的 HTML 網頁外觀和 Apple 官方的 API documentation 非常相似。如果不喜歡的話還可以自訂範本。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2016/12/xiaomi-mi-box/index.html b/2016/12/xiaomi-mi-box/index.html index def2a17c..cf0d0c53 100644 --- a/2016/12/xiaomi-mi-box/index.html +++ b/2016/12/xiaomi-mi-box/index.html @@ -4,19 +4,15 @@ 小米盒子國際版 -">

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。

小米盒子國際版
小米盒子國際版包裝盒背面

特色

  • Android TV 6
  • 內置 Google Play Services
  • 支援 Google Cast
  • 支援 4K 輸出

操控

跟機提供了一個搖控,這個遙控是操控盒子的主要方式。用法都是用十字掣、返回掣和主頁掣。另外,在部分頁面可以使用語音輸入(支援廣東話)。如果想輸入文字的話,可以用十字掣使用屏幕鍵盤,亦可以用背面的 USB Type A 插頭接駁電腦滑鼠和鍵盤。嫌麻煩的話可以在手機安裝 Android TV Remote Control,這樣就可以將手機的輸入法所打的文字傳送到 Android TV,這亦都是在 Android TV 輸入中文字的方法之一。

Apps

小米盒子國際版內置只有 Android TV 原裝的應用程式,而預先安裝的 app 只有兩個(Netflix 和 Red Bull TV)。它不會像中國版般一開箱就提供合法免費劇集和電影,亦不能看到中國版小米盒子的資源。使用界面和原裝 Android TV 一樣,但在設定的個別地方只有英文而沒有中文。

國際版內罝了 Google Play Store、Google Play Music、YouTube 等應用程式,但 Play Store 只會找到能在 Android TV 用到的 app,普通手機用到的 app 就不會找到。目前 Google Play Store 支援 Android TV 的 app 數目不算多,香港本地生產的 app 更是沒有。如果你想用小米盒子國際版來看本地電視節目的話基本上只能看 YouTube 或者用 Google Cast 將內容由手機投射到小米盒子。如果看到內置 Google Play 就想購買的話就要留意這點,可能會令你大失所望。

如果 Google Play Store 的 TV app 不能滿足你的話,可以選擇 side-load apk 檔。只需將 apk 抄到 USB 手指並在盒子安裝檔案管理程式,就可以用背面的 USB Type A 插頭來 side-load apk。但要留意即使你 side-load 到 apk 但不代表你能用到。因為不少 app 在開發時都沒有針對 d-pad 來設計,所以你可能會遇到一個情況:你明明看到界面有個按鈕,但你永遠都不能用遙控來點選它,除非用滑鼠。還有不少 app 在開發時 為求方便都會將屏幕鎖定到只能縱向顯示,但電視屏幕只能橫向顯示,結果整個畫面就被旋轉 90°,要躺在梳化才能操控。

藍牙手掣

小米藍牙手柄
小米藍牙手柄包裝盒背面

如果喜歡玩遊戲的話,可以考慮定價 HK$129 的小米藍牙手柄。因為一般 Android TV 裝置的遙控只有很少按鍵,所以不少 Android TV 遊戲都需要手掣才能玩。跟據官方的介紹,這個手掣支援 Android 設備,但不支援 iOS。外觀就仿照 Xbox 手掣。另外,手掣有雙摩打震動和三軸重力感應,但到目前為止都未見有 Android 遊戲支援震動和重力感應。震動器只會在藍牙配對成功時才會稍稍震動一下,其餘時間都不會震動。而重力感應更加未能體驗,如果小米網站無提及過的話根本沒有人會知道這個功能。

這個手掣另一樣特別之處就是可以在 Windows 當作 Xbox 360 手掣使用,變相令小米手掣更加實用。只需要安裝 Windows mapper 就能模擬成 Xbox 360 手掣。

結論

HK$499 的小米盒子國際版定價偏貴。如果你是衝着內置 Google Play 而購買小米盒子國際版的話,你很有可能失望。但如果想買 Chromecast Ultra 的話,可以考慮小米盒子國際版。因為價錢差不多,但小米盒子是用 Google TV,可以安裝電視 app,用途比 Chromecast Ultra 多。另外,盒子的容量很小,只有 5 GB 左右。而且玩遊戲時都有少許 lag 機,所以只適合玩一些比較簡單的遊戲。看影片的話都是以串流播放為主,如果你有訂閱 Netflix、在 Google Play 租用影片或者有 NAS 串流影片的話,都可以考慮入手。

而小米藍牙手柄是一個不錯的手掣,可以一雞兩味在 Android 和 Windows 使用,反而比盒子更有驚喜。

下面是試用時所拍的影片:

comments powered by Disqus
© 2024 EricLog +">

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。

小米盒子國際版
小米盒子國際版包裝盒背面

特色

  • Android TV 6
  • 內置 Google Play Services
  • 支援 Google Cast
  • 支援 4K 輸出

操控

跟機提供了一個搖控,這個遙控是操控盒子的主要方式。用法都是用十字掣、返回掣和主頁掣。另外,在部分頁面可以使用語音輸入(支援廣東話)。如果想輸入文字的話,可以用十字掣使用屏幕鍵盤,亦可以用背面的 USB Type A 插頭接駁電腦滑鼠和鍵盤。嫌麻煩的話可以在手機安裝 Android TV Remote Control,這樣就可以將手機的輸入法所打的文字傳送到 Android TV,這亦都是在 Android TV 輸入中文字的方法之一。

Apps

小米盒子國際版內置只有 Android TV 原裝的應用程式,而預先安裝的 app 只有兩個(Netflix 和 Red Bull TV)。它不會像中國版般一開箱就提供合法免費劇集和電影,亦不能看到中國版小米盒子的資源。使用界面和原裝 Android TV 一樣,但在設定的個別地方只有英文而沒有中文。

國際版內罝了 Google Play Store、Google Play Music、YouTube 等應用程式,但 Play Store 只會找到能在 Android TV 用到的 app,普通手機用到的 app 就不會找到。目前 Google Play Store 支援 Android TV 的 app 數目不算多,香港本地生產的 app 更是沒有。如果你想用小米盒子國際版來看本地電視節目的話基本上只能看 YouTube 或者用 Google Cast 將內容由手機投射到小米盒子。如果看到內置 Google Play 就想購買的話就要留意這點,可能會令你大失所望。

如果 Google Play Store 的 TV app 不能滿足你的話,可以選擇 side-load apk 檔。只需將 apk 抄到 USB 手指並在盒子安裝檔案管理程式,就可以用背面的 USB Type A 插頭來 side-load apk。但要留意即使你 side-load 到 apk 但不代表你能用到。因為不少 app 在開發時都沒有針對 d-pad 來設計,所以你可能會遇到一個情況:你明明看到界面有個按鈕,但你永遠都不能用遙控來點選它,除非用滑鼠。還有不少 app 在開發時 為求方便都會將屏幕鎖定到只能縱向顯示,但電視屏幕只能橫向顯示,結果整個畫面就被旋轉 90°,要躺在梳化才能操控。

藍牙手掣

小米藍牙手柄
小米藍牙手柄包裝盒背面

如果喜歡玩遊戲的話,可以考慮定價 HK$129 的小米藍牙手柄。因為一般 Android TV 裝置的遙控只有很少按鍵,所以不少 Android TV 遊戲都需要手掣才能玩。跟據官方的介紹,這個手掣支援 Android 設備,但不支援 iOS。外觀就仿照 Xbox 手掣。另外,手掣有雙摩打震動和三軸重力感應,但到目前為止都未見有 Android 遊戲支援震動和重力感應。震動器只會在藍牙配對成功時才會稍稍震動一下,其餘時間都不會震動。而重力感應更加未能體驗,如果小米網站無提及過的話根本沒有人會知道這個功能。

這個手掣另一樣特別之處就是可以在 Windows 當作 Xbox 360 手掣使用,變相令小米手掣更加實用。只需要安裝 Windows mapper 就能模擬成 Xbox 360 手掣。

結論

HK$499 的小米盒子國際版定價偏貴。如果你是衝着內置 Google Play 而購買小米盒子國際版的話,你很有可能失望。但如果想買 Chromecast Ultra 的話,可以考慮小米盒子國際版。因為價錢差不多,但小米盒子是用 Google TV,可以安裝電視 app,用途比 Chromecast Ultra 多。另外,盒子的容量很小,只有 5 GB 左右。而且玩遊戲時都有少許 lag 機,所以只適合玩一些比較簡單的遊戲。看影片的話都是以串流播放為主,如果你有訂閱 Netflix、在 Google Play 租用影片或者有 NAS 串流影片的話,都可以考慮入手。

而小米藍牙手柄是一個不錯的手掣,可以一雞兩味在 Android 和 Windows 使用,反而比盒子更有驚喜。

下面是試用時所拍的影片:

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/02/xiaomi-bluetooth-gamepad/index.html b/2017/02/xiaomi-bluetooth-gamepad/index.html index 8397a5f3..faa0b381 100644 --- a/2017/02/xiaomi-bluetooth-gamepad/index.html +++ b/2017/02/xiaomi-bluetooth-gamepad/index.html @@ -1,7 +1,7 @@ 小米藍牙手柄 | EricLog -

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。

除了 desktop game 之外,原來 HTML5 有 Gamepad API。我特別在 HTML5 Gamepad Tester 試了一次(未有用 Xbox mapper)。

  • Vendor ID: 2717
  • Product ID: 3144

其他按鍵的 keycode 可以看下面的影片:

comments powered by Disqus
© 2024 EricLog +

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。

除了 desktop game 之外,原來 HTML5 有 Gamepad API。我特別在 HTML5 Gamepad Tester 試了一次(未有用 Xbox mapper)。

  • Vendor ID: 2717
  • Product ID: 3144

其他按鍵的 keycode 可以看下面的影片:

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/04/android-signing-config/index.html b/2017/04/android-signing-config/index.html index 61587b21..47f5e1e7 100644 --- a/2017/04/android-signing-config/index.html +++ b/2017/04/android-signing-config/index.html @@ -1,5 +1,5 @@ Android 隱藏 signing config | EricLog -

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門的方式來設定 release keystore 和密碼。而在 VCS checkout source code 後在 file system 補上 keystore.properties 和 keystore 未必可以在 CI 環境上做到。所以我將那個教學稍作改動,令到當 keystore.properties 不存在時就不提供 signing config,使它能 build 未加簽的 apk,然後才讓 CI 加簽 apk。

apply plugin: 'com.android.application'
+

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門的方式來設定 release keystore 和密碼。而在 VCS checkout source code 後在 file system 補上 keystore.properties 和 keystore 未必可以在 CI 環境上做到。所以我將那個教學稍作改動,令到當 keystore.properties 不存在時就不提供 signing config,使它能 build 未加簽的 apk,然後才讓 CI 加簽 apk。

apply plugin: 'com.android.application'
 
 // Release signing properties
 def keystorePropertiesFile = rootProject.file("keystore.properties");
@@ -60,8 +60,8 @@
 storePassword = mypassword
 keyAlias = myalias
 keyPassword = mypassword
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2017/04/spek/index.html b/2017/04/spek/index.html index 5cbd8f0f..0781b686 100644 --- a/2017/04/spek/index.html +++ b/2017/04/spek/index.html @@ -1,8 +1,8 @@ Spek | EricLog

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。

Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。

安裝方法

其實 Spek 的網站有介紹過安裝方法,不過在 Android project 上使用要有些改變。

首先你的 Android project 要加入 Kotlin。這個部分 IDE 可以自動為你完成。之後就是加入 Spek。在 app 的 build.gradledependencies 加入以下部分:

dependencies {
+Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。">

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。

Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。

安裝方法

其實 Spek 的網站有介紹過安裝方法,不過在 Android project 上使用要有些改變。

首先你的 Android project 要加入 Kotlin。這個部分 IDE 可以自動為你完成。之後就是加入 Spek。在 app 的 build.gradledependencies 加入以下部分:

dependencies {
     testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
     testCompile("org.jetbrains.spek:spek-api:$spek_version") {
         exclude group: 'org.jetbrains.kotlin'
@@ -48,8 +48,8 @@
         assertEquals(Station.SHM.toLong(), fareTrip.from.toLong())
     }
 }
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2017/05/timber-live-template/index.html b/2017/05/timber-live-template/index.html index 82a53c2e..f5663827 100644 --- a/2017/05/timber-live-template/index.html +++ b/2017/05/timber-live-template/index.html @@ -1,7 +1,7 @@ Timber live template for Java/Kotlin | EricLog -

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logdlogm 之類的 Live templateAnkoAnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。

Live template 示範

Live template 我已經放到 Gist,是兩個 XML 檔來的。一個是 Java 版一個是 Kotlin 版。大致上和原裝的 live template 相似,只是將 log 改成 tim。例如 timd 會生成 Timber.d

安裝 live template 的方法是把這些 XML 檔案放到 IDE 的設定目錄內的 templates 目錄,不同作業系統的設定目錄位置都會不同。如果找不到 templates 目錄就自己建立一個。把檔案放到指定位置後重新開啟 IDE 便會生效。


2020 年 8 月 18 日更新: 如果是用 macOS 而又用 Android Studio 的話,安裝的路徑改為 /Users/xxx/Library/Application Support/Google/AndroidStudioPreview4.2/templates(將 AndroidStudioPreview4.2 換成你使用的版本)。

2023 年 1 月 24 日更新: Windows 版 Android Studio Electric Eel (2022.1.1) 的安裝路徑是 %APPDATA%\Google\AndroidStudio2022.1\templates,其他版本的路徑則需改變 AndroidStudio2022.1 的部分。

comments powered by Disqus
© 2024 EricLog +

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logdlogm 之類的 Live templateAnkoAnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。

Live template 示範

Live template 我已經放到 Gist,是兩個 XML 檔來的。一個是 Java 版一個是 Kotlin 版。大致上和原裝的 live template 相似,只是將 log 改成 tim。例如 timd 會生成 Timber.d

安裝 live template 的方法是把這些 XML 檔案放到 IDE 的設定目錄內的 templates 目錄,不同作業系統的設定目錄位置都會不同。如果找不到 templates 目錄就自己建立一個。把檔案放到指定位置後重新開啟 IDE 便會生效。


2020 年 8 月 18 日更新: 如果是用 macOS 而又用 Android Studio 的話,安裝的路徑改為 /Users/xxx/Library/Application Support/Google/AndroidStudioPreview4.2/templates(將 AndroidStudioPreview4.2 換成你使用的版本)。

2023 年 1 月 24 日更新: Windows 版 Android Studio Electric Eel (2022.1.1) 的安裝路徑是 %APPDATA%\Google\AndroidStudio2022.1\templates,其他版本的路徑則需改變 AndroidStudio2022.1 的部分。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/06/kotlin-for-android/index.html b/2017/06/kotlin-for-android/index.html index 21dc4c5a..eacfbb35 100644 --- a/2017/06/kotlin-for-android/index.html +++ b/2017/06/kotlin-for-android/index.html @@ -1,5 +1,5 @@ Kotlin for Android | EricLog -

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。

初初轉用時都有些地方不明白,需要經常查閱文檔和 Google 例子。但其實 Kotlin 都不算太難學,syntax 上和 Java 有不同但差異不算太大,再加上一些當代語言常見的特性。所以如果本身有學過其他語言的話會很快上手。Kotlin 誕生的原因是 JetBrains 用 Java 開發 IDE 時發現到 Java 的不足而令他們決心做一個新的語言,所以骨子裏有着 Java 的影子,而 Kotlin 本身都是 JVM language(即是 Kotlin 原碼會變成 JVM bytecode 然後用 JVM 來執行)。現在 Kotlin 除了 compile 成 JVM bytecode 之外,還可以轉換成 JavaScript 和 native(即是直接在作業系統上執行,不需要 JVM/Node)。

val & Nullable

Kotlin 的 nullable 和 var/val 特性令 declare variable 時要考慮到它是不是 mutable 和能否 null(其實 val 只是代表只可以 assign 一次,並不完全代表它是 immutable)。這些特性在開發時很有用,因為在使用這些 variable 時就能肯定它會不會 null 和會不會中途被改變 reference。而它會在 compile 時會檢查你的 code 是不是合符 variable 的 nullable 和 var/val 定義,那就避免在執行時才出現 NullPointerException。如果它是 nullable 的話,Kotlin 有 ?. (safe navigation operator)、?: (null coalescing operator/Elvis operator) 和 if 來處理,比起 Java 要加大量的 if 和 Java 8 的 Optional 更加簡潔。

Lambda

Lambda 可能是在 Android 未正式公布 Java 8 support 前轉用 Kotlin 的主要原因。因為 Android 有不少 callback 是 SAM-type (single abstract method),加上近期流行的 RxJava 都要用到大量 SAM-type,如果每次都要寫完整的 anonymous class 就會變得很長(雖然 IDE 可以將 code 摺起讓它看起來像 lambda)。Java 8 的 lambda 正好就能簡化這種寫法。在未有 Java 8 support 時,就要用 Retrolambda 來令 Java 7 可以用 Java 8 lambda syntax。不過現在 Android 都開始支援在舊 Android 版本用 lambda,lambda 現在未必能吸引 Android developer 轉用 Kotlin。不過 Kotlin 和 Android Java 不同之處就是 Kotlin 不是靠 Android 版本來決能你能否使用新的語言特性,所以你不用等 Android API level 24 成為 project 最低支援版本時才能使用 mapreduce 之類的功能,只需要轉用 Kotlin 就能馬上使用。日後 Kotlin 有新功能時只需要改一下 Gradle file 就能用新功能。

Java Interop

開發 Android app 必定要調用 Java class(Android SDK 和其他第三方 library),所以 Kotlin/Java 能否混合使用是非常重要。基本上在 Android 上用 Kotlin 都沒有發現有大問題。因為 Kotlin 本身就是可以和 Java 混合使用,你可以照常在 project 使用 Java 寫的 library。在 Kotlin 用 Java 的 class 是不需要太多特別處理,只是在 override method 時 parameter 可能會全部都當成 nullable(即是 parameter type 加了 ?)。這是因為這個 Java class 的 method parameter 未有加上 @Nullable@Nonnull annotation,所以 IDE 會假設全部 parameter 都是可以 null 的。如果你確定它不會 null 的話可以把 ? 拿走。現在 Square 出的 open source library 會開始加上 @Nullable annotation,期望 Android SDK 日後都會補回 @Nullable,同時方便用 Java 和 Kotlin 的人。如果是在 Java 用 Kotlin 的 class/function 的話,就需要留意是否需要補回 annotation 來控制 Java 一方所見到的效果(例如 class 名、是否需要用 static、constructor 是否需要窮舉所有 parameter 組合等等)。此外,Kotlin 有 kapt 做 annotation processor,在 Java 用的 aptannotationProcessor 可以用 kapt 代替。

Extension

Kotlin 的 extension 是用來為現成的 class 加入新 method 和 attribute。Extension 是用來取代 Java programming 時常寫的 Utils static method。Extension 比 Utils 較佳是 extension 有 IDE 提示和用法較自然。

舉個例子:FirebaseAnalytics 本來是有一個 logEvent 的 method,不過第二個 parameter 是 Bundle,使用時要預先準備好 Bundle 比較不方便。參考了 Anko startActivity做法,做了一個 extension function。

fun FirebaseAnalytics.logEvent(name: String, vararg params: Pair<String, Any>) {
+

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。

初初轉用時都有些地方不明白,需要經常查閱文檔和 Google 例子。但其實 Kotlin 都不算太難學,syntax 上和 Java 有不同但差異不算太大,再加上一些當代語言常見的特性。所以如果本身有學過其他語言的話會很快上手。Kotlin 誕生的原因是 JetBrains 用 Java 開發 IDE 時發現到 Java 的不足而令他們決心做一個新的語言,所以骨子裏有着 Java 的影子,而 Kotlin 本身都是 JVM language(即是 Kotlin 原碼會變成 JVM bytecode 然後用 JVM 來執行)。現在 Kotlin 除了 compile 成 JVM bytecode 之外,還可以轉換成 JavaScript 和 native(即是直接在作業系統上執行,不需要 JVM/Node)。

val & Nullable

Kotlin 的 nullable 和 var/val 特性令 declare variable 時要考慮到它是不是 mutable 和能否 null(其實 val 只是代表只可以 assign 一次,並不完全代表它是 immutable)。這些特性在開發時很有用,因為在使用這些 variable 時就能肯定它會不會 null 和會不會中途被改變 reference。而它會在 compile 時會檢查你的 code 是不是合符 variable 的 nullable 和 var/val 定義,那就避免在執行時才出現 NullPointerException。如果它是 nullable 的話,Kotlin 有 ?. (safe navigation operator)、?: (null coalescing operator/Elvis operator) 和 if 來處理,比起 Java 要加大量的 if 和 Java 8 的 Optional 更加簡潔。

Lambda

Lambda 可能是在 Android 未正式公布 Java 8 support 前轉用 Kotlin 的主要原因。因為 Android 有不少 callback 是 SAM-type (single abstract method),加上近期流行的 RxJava 都要用到大量 SAM-type,如果每次都要寫完整的 anonymous class 就會變得很長(雖然 IDE 可以將 code 摺起讓它看起來像 lambda)。Java 8 的 lambda 正好就能簡化這種寫法。在未有 Java 8 support 時,就要用 Retrolambda 來令 Java 7 可以用 Java 8 lambda syntax。不過現在 Android 都開始支援在舊 Android 版本用 lambda,lambda 現在未必能吸引 Android developer 轉用 Kotlin。不過 Kotlin 和 Android Java 不同之處就是 Kotlin 不是靠 Android 版本來決能你能否使用新的語言特性,所以你不用等 Android API level 24 成為 project 最低支援版本時才能使用 mapreduce 之類的功能,只需要轉用 Kotlin 就能馬上使用。日後 Kotlin 有新功能時只需要改一下 Gradle file 就能用新功能。

Java Interop

開發 Android app 必定要調用 Java class(Android SDK 和其他第三方 library),所以 Kotlin/Java 能否混合使用是非常重要。基本上在 Android 上用 Kotlin 都沒有發現有大問題。因為 Kotlin 本身就是可以和 Java 混合使用,你可以照常在 project 使用 Java 寫的 library。在 Kotlin 用 Java 的 class 是不需要太多特別處理,只是在 override method 時 parameter 可能會全部都當成 nullable(即是 parameter type 加了 ?)。這是因為這個 Java class 的 method parameter 未有加上 @Nullable@Nonnull annotation,所以 IDE 會假設全部 parameter 都是可以 null 的。如果你確定它不會 null 的話可以把 ? 拿走。現在 Square 出的 open source library 會開始加上 @Nullable annotation,期望 Android SDK 日後都會補回 @Nullable,同時方便用 Java 和 Kotlin 的人。如果是在 Java 用 Kotlin 的 class/function 的話,就需要留意是否需要補回 annotation 來控制 Java 一方所見到的效果(例如 class 名、是否需要用 static、constructor 是否需要窮舉所有 parameter 組合等等)。此外,Kotlin 有 kapt 做 annotation processor,在 Java 用的 aptannotationProcessor 可以用 kapt 代替。

Extension

Kotlin 的 extension 是用來為現成的 class 加入新 method 和 attribute。Extension 是用來取代 Java programming 時常寫的 Utils static method。Extension 比 Utils 較佳是 extension 有 IDE 提示和用法較自然。

舉個例子:FirebaseAnalytics 本來是有一個 logEvent 的 method,不過第二個 parameter 是 Bundle,使用時要預先準備好 Bundle 比較不方便。參考了 Anko startActivity做法,做了一個 extension function。

fun FirebaseAnalytics.logEvent(name: String, vararg params: Pair<String, Any>) {
     check(params.size <= 25) { "An event can have up to 25 parameters" }
 
     if (params.isEmpty()) {
@@ -31,8 +31,8 @@
 
 val Fragment.myApp: MyApplication
     get() = activity.applicationContext as MyApplication

這兩個 extension property 可以省卻每次在 ActivityFragment 使用 MyApplication 時都要做 type casting。

Data Class

Data class 主要用來替代平時儲 data 的 POJO。Compile 時會自動生成 getter/setter、toStringhashCodeequalscomponentN function、copy,令行數大大減少。在 Google I/O 宣布 Kotlin 成為 Android first-class support language 時不少網站都以 data class 作例子,所以不詳細介紹了。

DSL

Kotlin 有一個「得意」的功能,就是可以做 DSL (domain-specific language),Kotlin 官方文件稱為 type-safe builder。例子有 Anko(用來取代 XML 做 Android UI layout)、Spek(用來寫 testing specification,類似 RSpec)、kotlinx.html(用來生成 HTML 的 DSL)等等。不過又不需要 librray 級的 project 才要做 DSL,有時 project 中需要做一些巢狀的 object(比如類似樹狀的 object),那時用 DSL 就可以用直觀的方式建立整個 tree 而不需要寫一大堆 create object 再 add to list 的 code。

做 DSL 主要是用到 Kotlin 以下的特性:

官方文件對 DSL 沒有太深入的講解,反而 Kotlin in Action 第 11 章有詳細講解如何做 DSL,而這一章正好提供免費試讀。有需要的話可以看看。

不足的地方

基本上都沒發現太大的問題,只是配套上有所不足。例如 IntelliJ IDEA/Android Studio 內置的 code coverage runner 在計算 branch coverage 會不準確。

另一個問題是 data class 和 JSON/XML serialization/deserialization library 未能全面配合,其實這個問題不太關 Kotlin 事。Data class 容許定義 default value、nullable,不過有不少 JSON/XML serialization/deserialization library 都是用 Java reflection 來做轉換。有些 library 會支援 Kotlin data class(例如 moshi-kotlinjackson-module-kotlin),但需要用到 kotlin-reflect 這個 dependency(這個 dependency 有過萬個 method,對 Android 來說是很大)。

小貼士

  • 可以善用 IDE 的「Show Kotlin bytecode」功能,看看生成的 Java bytecode。不懂看的話可以按一下「Decompile」,IDE 會將 bytecode 變成 Java。可以用這個功能來看看生成的 Java code。亦可以用這個功能來對比 Kotlin 和 Java 的分別。
  • IDE 有「Kotlin REPL」,可以用來簡單試一下 Kotlin 的用法,不用等待 IDE build project。
  • 目前 Google Samples 的 GitHub 開始有同時提供 Java 和 Kotlin 的 demo project,不過 Kotlin 版好像是自動轉換出來的,寫得不太好,所以目前都是看 Java 版比較好。
  • Android 的 Activity、Fragment、Service 等等都不是用 constructor 來 initialize variable,但又不想將它變成 nullable 的話,可以善用 lateinitDelegates.notNull 來押後 initialize variable。
  • 如果是想用於 Android 開發而沒有學過 Java 的話,還是先學 Java 才學 Kotlin。因為 Android SDK 是 Java 而且第三方 library 都是 Java 居多。即使有提供 Kotlin 版大多只是提供 extension function 而不是完全用 Kotlin 重寫一遍。
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2017/06/semver/index.html b/2017/06/semver/index.html index 669342b3..1e390f98 100644 --- a/2017/06/semver/index.html +++ b/2017/06/semver/index.html @@ -1,7 +1,7 @@ SemVer | EricLog -

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 equals 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。

comments powered by Disqus
© 2024 EricLog +

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 equals 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/06/trainboard-for-android-tv/index.html b/2017/06/trainboard-for-android-tv/index.html index b0c3334d..c89c3818 100644 --- a/2017/06/trainboard-for-android-tv/index.html +++ b/2017/06/trainboard-for-android-tv/index.html @@ -1,7 +1,7 @@ TrainBoard for Android TV | EricLog -

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。TrainBoard 提供港鐵將軍澳綫、東涌綫、機場快綫、迪士尼綫及西鐵綫列車的預計到達時間,而這個 app 是為 MTR Service Update 而做的。

TrainBoard on Google Play Store
在 Android TV 的 Play Store 搜尋「TrainBoard」就會找到

這個 app 用法其實很簡單,只需要在主頁揀選車站,再揀選方向,你的電視就會變成好像車站月台的顯示屏般顯示下五班車的預計到站時間。如果因為空間不夠而看不到五筆資料的話,可以按遙控的 D-pad(方向掣)。

主頁
Live ETA

上架過程

其實這篇文除了介紹 TrainBoard 之外,還想講一下上架的過程。在 Google Play Store 把 app 上架向來都是不用審批,不過自從有了 Android TV 和 Android Wear 之後,Android TV 和 Android Wear app 就要先經過審批才能真正上架。以 Android TV 為例,不送審其實都可以把 Android TV 上架,但只限於網頁版,Android TV 上的 Google Play Store 是找不到這個 app(即是只可以經網頁版下載安裝)。這亦都解釋了為甚麼 Android TV 上的 Google Play Store 只有很少 app 可以下載。

TrainBoard 其實在上星期日交上 Play Store,到了星期二出了審批結果被拒上架。原因是沒有 full-size app banner

We are targeting 1080P, which we consider xhdpi. Apps should include the banner in the xhdpi (320 dpi) drawables folder with a size of (320px × 180px).

不過我檢查過 drawable-xhdpi 確實有合符尺寸的 banner 圖,而 manifest 亦都有在 <application> 加上 android:banner attribute。

未能通過審批的 banner

之後我填表格上訴,然後他們回覆我被拒的原因不是尺寸問題,而是背景顏色問題。

I understand your app recently wasn’t approved for Android TV recently for a banner issue. Looking into it it appears that the issue may have been misrepresented on our side a little. The issue is the size because the banner is a gray box. If you could make the box white or green, etc. where it sticks out more I believe that will solve your issue. A gray box isn’t accepted because it blends in with the home page too much. Once this is updated please resubmit your app and we will gladly review it again for you.

James H, Google Play Developer Support

看完回覆之後馬上改一改背景顏色,果然第二日(星期四)就能順利上架。

順利上架的 app banner

只限 Android TV?

是的,因為 TrainBoard 只用了 Leanback library 來做 UI,沒有為手機和平版設計過另一套 UI,即使安裝到手機或平版上效果都不理想(有些地方要用 D-pad 才能操作),所以就不讓手機和平版使用這個 app。但很快就會將 TrainBoard 移植到 Android 手機和平版上使用,請耐心等候,或者用網頁版也可以。

comments powered by Disqus
© 2024 EricLog +

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。TrainBoard 提供港鐵將軍澳綫、東涌綫、機場快綫、迪士尼綫及西鐵綫列車的預計到達時間,而這個 app 是為 MTR Service Update 而做的。

TrainBoard on Google Play Store
在 Android TV 的 Play Store 搜尋「TrainBoard」就會找到

這個 app 用法其實很簡單,只需要在主頁揀選車站,再揀選方向,你的電視就會變成好像車站月台的顯示屏般顯示下五班車的預計到站時間。如果因為空間不夠而看不到五筆資料的話,可以按遙控的 D-pad(方向掣)。

主頁
Live ETA

上架過程

其實這篇文除了介紹 TrainBoard 之外,還想講一下上架的過程。在 Google Play Store 把 app 上架向來都是不用審批,不過自從有了 Android TV 和 Android Wear 之後,Android TV 和 Android Wear app 就要先經過審批才能真正上架。以 Android TV 為例,不送審其實都可以把 Android TV 上架,但只限於網頁版,Android TV 上的 Google Play Store 是找不到這個 app(即是只可以經網頁版下載安裝)。這亦都解釋了為甚麼 Android TV 上的 Google Play Store 只有很少 app 可以下載。

TrainBoard 其實在上星期日交上 Play Store,到了星期二出了審批結果被拒上架。原因是沒有 full-size app banner

We are targeting 1080P, which we consider xhdpi. Apps should include the banner in the xhdpi (320 dpi) drawables folder with a size of (320px × 180px).

不過我檢查過 drawable-xhdpi 確實有合符尺寸的 banner 圖,而 manifest 亦都有在 <application> 加上 android:banner attribute。

未能通過審批的 banner

之後我填表格上訴,然後他們回覆我被拒的原因不是尺寸問題,而是背景顏色問題。

I understand your app recently wasn’t approved for Android TV recently for a banner issue. Looking into it it appears that the issue may have been misrepresented on our side a little. The issue is the size because the banner is a gray box. If you could make the box white or green, etc. where it sticks out more I believe that will solve your issue. A gray box isn’t accepted because it blends in with the home page too much. Once this is updated please resubmit your app and we will gladly review it again for you.

James H, Google Play Developer Support

看完回覆之後馬上改一改背景顏色,果然第二日(星期四)就能順利上架。

順利上架的 app banner

只限 Android TV?

是的,因為 TrainBoard 只用了 Leanback library 來做 UI,沒有為手機和平版設計過另一套 UI,即使安裝到手機或平版上效果都不理想(有些地方要用 D-pad 才能操作),所以就不讓手機和平版使用這個 app。但很快就會將 TrainBoard 移植到 Android 手機和平版上使用,請耐心等候,或者用網頁版也可以。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/09/parcelable-intent-extra/index.html b/2017/09/parcelable-intent-extra/index.html index d9320487..aa3e207f 100644 --- a/2017/09/parcelable-intent-extra/index.html +++ b/2017/09/parcelable-intent-extra/index.html @@ -1,7 +1,7 @@ Parcelable & Intent extra | EricLog -

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 ActivityFragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable

最近寫 Android app 時無意中發現 IntentgetParcelableExtra return 出來的 object 是會重用的。如果我有個 object 用 Parcelable intent extra 在 Activity A 傳去 Activity B,而在 Activity B 用 getParcelableExtra 取回這個 object 然後再改一下 object 的 field,之後返回 Activity A 再傳相同的 object 去 Activity B,用 getParcelableExtra 取回這個 object 是會取得先前在 Activity B 改動過的 object,而不是 Activity A 那個原本的 object。

所以如果打算會改動 getParcelableExtra 傳回的 object 的話,最好都是複製一個來改,不要直接改傳回的 object。如果是 Kotlin data object 的話,可以用 copy 這個 method。

另外,Kotlin 1.1.4 的 Android Extensions plugin 新增了 @Parcelize annotation 來自動生成 Parcelable 相關的 code。但它是實驗功能,還未有正式說明文檔。如果不想用實驗功能的話,可以用 PaperParcel 之類的 library。

comments powered by Disqus
© 2024 EricLog +

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 ActivityFragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable

最近寫 Android app 時無意中發現 IntentgetParcelableExtra return 出來的 object 是會重用的。如果我有個 object 用 Parcelable intent extra 在 Activity A 傳去 Activity B,而在 Activity B 用 getParcelableExtra 取回這個 object 然後再改一下 object 的 field,之後返回 Activity A 再傳相同的 object 去 Activity B,用 getParcelableExtra 取回這個 object 是會取得先前在 Activity B 改動過的 object,而不是 Activity A 那個原本的 object。

所以如果打算會改動 getParcelableExtra 傳回的 object 的話,最好都是複製一個來改,不要直接改傳回的 object。如果是 Kotlin data object 的話,可以用 copy 這個 method。

另外,Kotlin 1.1.4 的 Android Extensions plugin 新增了 @Parcelize annotation 來自動生成 Parcelable 相關的 code。但它是實驗功能,還未有正式說明文檔。如果不想用實驗功能的話,可以用 PaperParcel 之類的 library。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2017/10/android-studio-3-gradle/index.html b/2017/10/android-studio-3-gradle/index.html index 2b9aad89..c96f2312 100644 --- a/2017/10/android-studio-3-gradle/index.html +++ b/2017/10/android-studio-3-gradle/index.html @@ -1,5 +1,5 @@ Android Studio 3 的 Gradle 更新 | EricLog -

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。

Android Studio 3 的更新通知

Android Studio 升級完成後,開啟 project。Android Studio 會提示你升級 build tool 之類的東西,按照指示進行。Android Studio 會改動你的 Gradle wrapper,build tool 版本。但之後 Gradle project sync 時可能會有一大堆 error。這時可以試試以下的方法:

檢查 app/build.gradlebuildToolsVersion 是否太舊。在根目錄的 build.gradleext 內加入 compileSdkVersionbuildToolsVersion

ext {
+

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。

Android Studio 3 的更新通知

Android Studio 升級完成後,開啟 project。Android Studio 會提示你升級 build tool 之類的東西,按照指示進行。Android Studio 會改動你的 Gradle wrapper,build tool 版本。但之後 Gradle project sync 時可能會有一大堆 error。這時可以試試以下的方法:

檢查 app/build.gradlebuildToolsVersion 是否太舊。在根目錄的 build.gradleext 內加入 compileSdkVersionbuildToolsVersion

ext {
     compileSdkVersion = 26
     buildToolsVersion = '26.0.2'
     // ...
@@ -95,8 +95,8 @@
     testImplementation 'junit:junit:4.12'
     testImplementation "org.robolectric:robolectric:3.4.2"
 }

註:kapt 是 Kotlin annotation processing tool,如果是 Java project 應該用 annotationProcessor

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2017/12/react-native-multi-window/index.html b/2017/12/react-native-multi-window/index.html index 58f68985..33949d68 100644 --- a/2017/12/react-native-multi-window/index.html +++ b/2017/12/react-native-multi-window/index.html @@ -1,5 +1,5 @@ React Native Android Multi-window 多視窗支援 | EricLog -

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。

之後在 AndroidManifest.xml 應該會找到下面類似的 <activity>

<activity
+

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。

之後在 AndroidManifest.xml 應該會找到下面類似的 <activity>

<activity
     android:name=".MainActivity"
     android:label="@string/app_name"
     android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
@@ -18,8 +18,8 @@
         <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
 </activity>

之後就可以支援這個功能了。

修改 android:configChanges 的原因是因為 Android 在進行 Multi-window 動作時(例如由一個視窗變成兩個視窗並排顯示時、視窗尺寸改變時),都會被當作 configuration change 處理。即是 activity 會執行 onDestroy 之後再執行 onCreate。但 React Native 是由它自己處理 configuration change,所以 React Native 的 project 就在 <activity> 加入 android:configChanges,令原本因旋轉畫面之類的 configuration change 都不會執行 onDestroyonCreate,重新整理 activity 界面就交由 React Native 處理。但 Multi-window 是有新的 android:configChanges 常數,所以現在就要補回,否則用 Multi-window 會把 React Native app 的狀態掉失。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2018/02/parcelize/index.html b/2018/02/parcelize/index.html index b0e692e2..ad9ad1b0 100644 --- a/2018/02/parcelize/index.html +++ b/2018/02/parcelize/index.html @@ -1,5 +1,12 @@ Kotlin Parcelize | EricLog -

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。

@Parcelize
+

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。

@Parcelize
 data class Product(val name: String, val price: Double) : Parcelable

留意要在 build.gradle 加上:

androidExtensions {
     experimental = true
 }

這個基本用法在不少網站都有介紹過,的確可以節省不少時間 copy and paste code,或者可以不需要再用 PaperParcel 之類的 library。不過如果要自訂個別 property 的 adapter 的話(例如那個 property 的 data type 不是自己的 class 又沒有 implement Parcelable),就可以用 @WriteWith 來註明 adapter:

@Parcelize
@@ -14,8 +21,8 @@
         parcel.writeString(this.fullDate)
     }
 }

Parceler 有兩個 method 要實作:一個是從 ExpiryDate serialize 變成 Parcel;另一個是由 Parcel deserialize 變成 ExpiryDate。這個例子用了 string 來 serialize,你可以用其他 type 來 serialize,最重要是之後可以被還原。留意必須使用 object class。

如果 ExpiryDate 是 nullable 的話,在 ExpiryDateParcelerExpiryDate 改成 ExpiryDate? 即可。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2018/03/linkify/index.html b/2018/03/linkify/index.html index 6db6d52b..43bff840 100644 --- a/2018/03/linkify/index.html +++ b/2018/03/linkify/index.html @@ -1,13 +1,13 @@ Linkify 自動轉換成網址 | EricLog -

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http://https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular expression 做檢查的話那句 regular expression 就會好長,而且要定時補上日後新推出的 TLD。

之後查過原來 Android 有 Linkify/LinkifyCompat 這個 class:

Linkify take a piece of text and a regular expression and turns all of the regex matches in the text into clickable links. This is particularly useful for matching things like email addresses, web URLs, etc. and making them actionable. Alone with the pattern that is to be matched, a URL scheme prefix is also required. Any pattern match that does not begin with the supplied scheme will have the scheme prepended to the matched text when the clickable URL is created. For instance, if you are matching web URLs you would supply the scheme http://. If the pattern matches example.com, which does not have a URL scheme prefix, the supplied scheme will be prepended to create http://example.com when the clickable URL link is created.

看起來應該合用,不過一般用法都是用來將 TextView 的合適文字變成可點擊的超連結。下面是我的做法:

val query = "example.com"
+

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http://https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular expression 做檢查的話那句 regular expression 就會好長,而且要定時補上日後新推出的 TLD。

之後查過原來 Android 有 Linkify/LinkifyCompat 這個 class:

Linkify take a piece of text and a regular expression and turns all of the regex matches in the text into clickable links. This is particularly useful for matching things like email addresses, web URLs, etc. and making them actionable. Alone with the pattern that is to be matched, a URL scheme prefix is also required. Any pattern match that does not begin with the supplied scheme will have the scheme prepended to the matched text when the clickable URL is created. For instance, if you are matching web URLs you would supply the scheme http://. If the pattern matches example.com, which does not have a URL scheme prefix, the supplied scheme will be prepended to create http://example.com when the clickable URL link is created.

看起來應該合用,不過一般用法都是用來將 TextView 的合適文字變成可點擊的超連結。下面是我的做法:

val query = "example.com"
 val spannable = SpannableString(query)
 LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS)
 val linkifyUrl = spannable.getSpans(0, spannable.length, URLSpan::class.java)
     .filter { urlSpan -> spannable.getSpanStart(urlSpan) == 0 && spannable.getSpanEnd(urlSpan) == spannable.length }
     .map { urlSpan -> urlSpan.url }
     .firstOrNull()

首先將疑似網址的 string 變成 SpannableString。之後用 LinkifyCompat#addLinks 來檢查整個 string 有沒有網址。如果有的話,SpannableString 中的網址部分會有 span 包住。

這次的要求是整個 query string 最多只有一個網址,如果沒有網址或出現多於一個網址的話,就當作沒有網址處理。所以要加上 filter 來篩走出現多於一個網址的情況。

最後 linkifyUrl 會是 http://example.com;如果 query 沒有網址的話,那 linkifyUrl 會是 null

至於 LinkifyCompat 會不會定期更新 TLD 清單,我不太清楚,但當初出現 LinkifyCompat 就是用來補回新出的 TLD

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2018/04/octopus-acquiring-business/index.html b/2018/04/octopus-acquiring-business/index.html index e8ea6e99..9e6bd703 100644 --- a/2018/04/octopus-acquiring-business/index.html +++ b/2018/04/octopus-acquiring-business/index.html @@ -1,10 +1,10 @@ 八達通新方向 | EricLog

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。

八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。

到了近年推出 O! ePay(八達通「好易畀」)並取得 SVF 牌照,正式進軍流動支付。不過 O! ePay 和八達通實體卡是兩個獨立的戶口。交易方法都有不同:O! ePay 是用 QR code 進行交易;實體卡則是用 RFID。因為一般商戶都是用實體卡拍卡機,只有甚少會支援 O! ePay QR code。所以 O! ePay 在消費方面不夠普及。其他功能比如朋友間轉賬、繳費等亦都因為用戶數量少、要另外申請戶口、增值 / 轉走餘額方式比較少(主要靠 NFC 從實體八達通卡轉出 / 轉入餘額)、市場上有其他同類的 app 等等的原因同樣少人用。結果 O! ePay 變成無本生利的信用卡積分 / 飛行里數刷分工具。

到近月在 Samsung Pay 支援 Smart Octopus 到近日 Apple Pay 支援北京上海公交卡又再令網民批評八達通落後,寧願支援 Samsung 幾個型號的設備都不肯支援多人用的 Apple Pay/Google Pay。可能因為八達通對 Apple/Google 抽成不滿意,而 Samsung 可能是貼錢請八達通支援 Samsung Pay。八達通向來都是靠按金、交易手續費、拍卡機租金、用戶私隱來賺錢,如果要跟其他公司分成可能就不太願意。

八達通最廣泛的用途是繳付車資,其次是零售商戶消費。尤其是交通這部分幾乎是壟斷,其他公司難以打入這一市場。八達通最大好處是離線交易,速度夠快。但這正正是它的缺點:因為要迎合近年的競爭,推出了和其他流動支付工具相近的 app。但用家卻因為用法不及實體卡方便和應用場合少而不使用。

八達通在小額交易,尤其是在交通方面可以繼續「食老本」。但如果可以「FF」的話,香港的交通運輸機構可以參考倫敦 Transport for London (TfL) / 廣州地鐵般提供非接觸式信用卡付款(即是 Offline Data Authentication),這樣就能用 Apple Pay/Android Pay 付款,不是經常訪港的遊客毋須購買八達通卡就能乘車。聽起來這樣會對八達通公司不利,但這可能是八達通的新商機。

因為香港的交通有不少跨機構的轉乘優惠(例如港鐵和小巴的轉乘優惠),而交通運輸機構並不是由單一機構控制。如果由交通運輸機構各自處理信用卡車費付款的話就不能做到這些優惠(因為在尖東及尖沙咀站轉車 / 月票 / 轉乘優惠 / 港鐵特惠站是靠八達通卡的儲存資料在拍卡時即時計算,而信用卡是不會儲存這些資料),而各交通運輸機構一直都有和八達通公司合作(它們本身就接受八達通付款)。所以交由八達通公司負責信用卡車費結算最為合理。和八達通卡的運作一樣,交通運輸機構每日都要為拍卡機「清機」,將拍卡機內的交易紀錄交到八達通公司。八達通公司收到這些紀錄後就可以按各交通運輸機構所訂的車費和轉乘優惠規則計算每張信用卡當日的總車費,再向信用卡發卡組織扣錢。如果遲交交易紀錄的話,就在之後的交易日少扣當日車費,日子相隔太遠的話就考慮直接退款。拍卡的動作和八達通一樣都是離線交易,但因為用信用卡不能即時計算實際車費,所以在拍卡時不能顯示所扣車費。故此,八達通公司要提供網上查閱已誌帳的交易紀錄,方便乘客查詢及核對。另外,因為信用卡未能儲存票種(成人、長者、學生),在查票時難以操作。所以只可以當作成人車費處理。簡單來說,使用信用卡乘車是先乘車,後付款。而八達通公司亦由只處理八達通卡交易變成同時兼任信用卡收單機構。如果和信用卡發卡組織洽談到一個較優惠的手續費的話,八達通公司可能有利可圖。繼而可以向商戶推銷八達通及信用卡二合一讀卡機,即使消費者不用八達通都可以有錢賺。

comments powered by Disqus
© 2024 EricLog +八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。">

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。

八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。

到了近年推出 O! ePay(八達通「好易畀」)並取得 SVF 牌照,正式進軍流動支付。不過 O! ePay 和八達通實體卡是兩個獨立的戶口。交易方法都有不同:O! ePay 是用 QR code 進行交易;實體卡則是用 RFID。因為一般商戶都是用實體卡拍卡機,只有甚少會支援 O! ePay QR code。所以 O! ePay 在消費方面不夠普及。其他功能比如朋友間轉賬、繳費等亦都因為用戶數量少、要另外申請戶口、增值 / 轉走餘額方式比較少(主要靠 NFC 從實體八達通卡轉出 / 轉入餘額)、市場上有其他同類的 app 等等的原因同樣少人用。結果 O! ePay 變成無本生利的信用卡積分 / 飛行里數刷分工具。

到近月在 Samsung Pay 支援 Smart Octopus 到近日 Apple Pay 支援北京上海公交卡又再令網民批評八達通落後,寧願支援 Samsung 幾個型號的設備都不肯支援多人用的 Apple Pay/Google Pay。可能因為八達通對 Apple/Google 抽成不滿意,而 Samsung 可能是貼錢請八達通支援 Samsung Pay。八達通向來都是靠按金、交易手續費、拍卡機租金、用戶私隱來賺錢,如果要跟其他公司分成可能就不太願意。

八達通最廣泛的用途是繳付車資,其次是零售商戶消費。尤其是交通這部分幾乎是壟斷,其他公司難以打入這一市場。八達通最大好處是離線交易,速度夠快。但這正正是它的缺點:因為要迎合近年的競爭,推出了和其他流動支付工具相近的 app。但用家卻因為用法不及實體卡方便和應用場合少而不使用。

八達通在小額交易,尤其是在交通方面可以繼續「食老本」。但如果可以「FF」的話,香港的交通運輸機構可以參考倫敦 Transport for London (TfL) / 廣州地鐵般提供非接觸式信用卡付款(即是 Offline Data Authentication),這樣就能用 Apple Pay/Android Pay 付款,不是經常訪港的遊客毋須購買八達通卡就能乘車。聽起來這樣會對八達通公司不利,但這可能是八達通的新商機。

因為香港的交通有不少跨機構的轉乘優惠(例如港鐵和小巴的轉乘優惠),而交通運輸機構並不是由單一機構控制。如果由交通運輸機構各自處理信用卡車費付款的話就不能做到這些優惠(因為在尖東及尖沙咀站轉車 / 月票 / 轉乘優惠 / 港鐵特惠站是靠八達通卡的儲存資料在拍卡時即時計算,而信用卡是不會儲存這些資料),而各交通運輸機構一直都有和八達通公司合作(它們本身就接受八達通付款)。所以交由八達通公司負責信用卡車費結算最為合理。和八達通卡的運作一樣,交通運輸機構每日都要為拍卡機「清機」,將拍卡機內的交易紀錄交到八達通公司。八達通公司收到這些紀錄後就可以按各交通運輸機構所訂的車費和轉乘優惠規則計算每張信用卡當日的總車費,再向信用卡發卡組織扣錢。如果遲交交易紀錄的話,就在之後的交易日少扣當日車費,日子相隔太遠的話就考慮直接退款。拍卡的動作和八達通一樣都是離線交易,但因為用信用卡不能即時計算實際車費,所以在拍卡時不能顯示所扣車費。故此,八達通公司要提供網上查閱已誌帳的交易紀錄,方便乘客查詢及核對。另外,因為信用卡未能儲存票種(成人、長者、學生),在查票時難以操作。所以只可以當作成人車費處理。簡單來說,使用信用卡乘車是先乘車,後付款。而八達通公司亦由只處理八達通卡交易變成同時兼任信用卡收單機構。如果和信用卡發卡組織洽談到一個較優惠的手續費的話,八達通公司可能有利可圖。繼而可以向商戶推銷八達通及信用卡二合一讀卡機,即使消費者不用八達通都可以有錢賺。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2018/10/google-apps-script-create-calendar-event/index.html b/2018/10/google-apps-script-create-calendar-event/index.html index 1ac8abdb..0d482197 100644 --- a/2018/10/google-apps-script-create-calendar-event/index.html +++ b/2018/10/google-apps-script-create-calendar-event/index.html @@ -1,5 +1,5 @@ 用 Google Apps Script 建立 Google Calendar event | EricLog -

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。

準備內容

首先,去 Google Drive 建立一個 Spreadsheet。

我們會將這些假期加到 Google Calendar 內

找出 Calendar ID

因為每人的 Google Calendar 可以有多於一個 calendar,所以第一件事就是要找到你想加入 event 那一個行事曆的 calendar ID。按 Tools > Script editor 進入 Google Apps Script 的編輯工具。

進入 Google Apps Script 的方法

這個編輯工具左邊是檔案列表,右邊是讓你修改程式碼。

我們首先試試下面的一個 function:

function listAllCalendars() {
+

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。

準備內容

首先,去 Google Drive 建立一個 Spreadsheet。

我們會將這些假期加到 Google Calendar 內

找出 Calendar ID

因為每人的 Google Calendar 可以有多於一個 calendar,所以第一件事就是要找到你想加入 event 那一個行事曆的 calendar ID。按 Tools > Script editor 進入 Google Apps Script 的編輯工具。

進入 Google Apps Script 的方法

這個編輯工具左邊是檔案列表,右邊是讓你修改程式碼。

我們首先試試下面的一個 function:

function listAllCalendars() {
   var calendars = CalendarApp.getAllCalendars();
   calendars.forEach(function (c) {
     Logger.log(c.getName() + " >>> " + c.getId())
@@ -41,8 +41,8 @@
   })
 }

CALENDAR_ID 就是最初抄下來的 calendar ID;holidays 就是剛才讀取 spreadsheet 的部分;template 就是建立之前準備好的 HTML template。

然後就為每筆記錄(公眾假期)套進 HTML template 並生成 HTML code,之後就用 createAllDayEvent(title, date, options) 來建立 all-day event(因為公眾假期是全日的,如果你的 event 不是全日的話就改用 createEvent(title, startTime, endTime, options) 或其他 method)。

HTML template 要把 template 內用過的 variable(即是 zhTitleenTitle)設定好才可以生成 HTML code(template.evaluate().getContent() 這一句)。

最後執行 createCalendarEvents 就可以建立 event。

成品效果
編輯 event 會看到 HTML 格式的內容

留意每執行一次 createCalendarEvents 就會建立新的 event 而不是修改現有的 event。

如果你想在再執行時跳過已建立過的 event 的話,可以在 spreadsheet 加多一個 column 標記來記錄這一筆資料是否已經建立過 event。下面兩句可供參考(記錄當前用戶電郵地址、時間):

sheet.getRange(row, column).setValue(Session.getActiveUser().getEmail())
 sheet.getRange(row, column).setValue(new Date())

完整程式碼

Google Apps Script 程式碼、HTML 和試算表內容已經放到 Gist 上。

下一篇會講解利用 Google Apps Script 發送電郵(即是 mail merge)。


補充

在處理時間時難免會想用 Moment.js 來格式化、計算時間,又或者用 Lodash 之類的 JavaScript library。Google Apps Script 是有機制 (App Script library) 讓你調用這些 library。詳情可以參考 Stack Overflow 的答案。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2019/02/openrefine-snippets/index.html b/2019/02/openrefine-snippets/index.html index 38bf01fe..441b08b8 100644 --- a/2019/02/openrefine-snippets/index.html +++ b/2019/02/openrefine-snippets/index.html @@ -7,7 +7,9 @@ OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。 -值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。">

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。

Text facet 功能可以批量修改相近的資料,而不用寫程式

OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。

值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。

移除𥤧白字元

value.replace(/\s+/,"")
+值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。">

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。

Text facet 功能可以批量修改相近的資料,而不用寫程式

OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。

值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。

移除𥤧白字元

value.replace(/\s+/,"")
 

HK1980 方格網坐標轉換成 WGS84

香港政府測量是用自己的 HK1980 方格網坐標,所以在香港政府資料一線通下載和坐標相關的資料都能找到 HK1980 坐標。最初他們只提供 HK1980 座標,近年才同時提供 WGS84 十進制坐標(即是 Google Maps API 用的坐標系統)。

如果你有 HK1980 坐標的話,可以在 OpenRefine 經地政總署提供的坐標轉換應用程式界面 (API)把坐標轉成 WGS84 十進制坐標。

假設你的表格有 hk80_northinghk80_easting 兩欄。在其中一欄點選右邊選單按鈕 > Edit column > Add column by fetching URLs…,然後輸入下面的表達式:

"http://www.geodetic.gov.hk/transform/v2/?inSys=hkgrid&n="+cells['hk80_northing'].value+"&e="+cells['hk80_easting'].value
 

之後新加入的一欄會有這個 API 的 JSON response。下面是以 (828386N, 815035E) 作示範:

{"wgsLat": 22.394601560,"wgsLong": 113.970677223,"hkLat": 22.396125640,"hkLong": 113.968227240,"utmGridZone": "49Q","utmGridE": 805878,"utmGridN": 2479527,"utmRefZone": "49Q-HE","utmRefE": "058","utmRefN": "795"}
 

OpenRefine 有 parse JSON 的 function,我們可以再在 JSON response 那一欄點選右邊選單按鈕 > Edit column > Add column based on this column…,然後輸入下面的表達式:

value.parseJson()["wgsLat"]
@@ -31,8 +25,8 @@
 

便可取得 WGS84 坐標。

當然,如果有大量坐標要轉換的話都是用程式轉換比較好。但數量不大的話這個方法比較方便。

街燈柱編號轉換

"https://www.map.gov.hk/gih-ws2/lp/"+value
 

例子:

[{"UTILITYNUMBER":"FA2385","UTILITYPOINTID":1107263606,"X":815027.67999,"Y":829842.619999999,"NAME":"Tuen Mun","NAME_C":"屯門"}]
 

X 是 HK1980 Easting;Y 是 HK1980 Norting。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2019/04/google-apps-script-send-email/index.html b/2019/04/google-apps-script-send-email/index.html index c448b6a8..49fb0546 100644 --- a/2019/04/google-apps-script-send-email/index.html +++ b/2019/04/google-apps-script-send-email/index.html @@ -1,5 +1,12 @@ 用 Google Apps Script 發送電郵 | EricLog -

用 Google Apps Script 發送電郵

上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。

準備內容

上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。

得獎名單

第二及第三欄是我們會在電郵內文用到;Sent By 和 Sent At 兩欄是用來判別這封電郵是否發送過。如果已經發送過就會標明是由誰人執行和執行時間。

準備內文範本

和之前一樣,都是用 Google Apps Script 的 HTML template 功能。

<p>Dear <?= name ?>,<p>
+

用 Google Apps Script 發送電郵

上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。

準備內容

上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。

得獎名單

第二及第三欄是我們會在電郵內文用到;Sent By 和 Sent At 兩欄是用來判別這封電郵是否發送過。如果已經發送過就會標明是由誰人執行和執行時間。

準備內文範本

和之前一樣,都是用 Google Apps Script 的 HTML template 功能。

<p>Dear <?= name ?>,<p>
 
 <p>Congratulation! You won our smartphone giveaway. Your <?= prize ?> will be shipped to you within one month.</p>

簽名檔 / 加入圖片(非必須)

使用 GmailApp 發送電郵是不會把你 Gmail 所設的簽名檔加到電郵內。如果你想在電郵內出現簽名檔的話,需要在 HTML template 內加入簽名檔的 HTML code。留意電郵軟件未必支援新式的 HTML/CSS,所以電郵排版都是用 HTML <table> 和用 inline CSS。

如果想加入圖片的話,有兩個方法:

  1. 直接連結寄存在其他地方的圖片
  2. 將圖片變成電郵附件,再顯示在電郵的內文

第一個方法和平時做網頁差不多,第二個方法比較複雜。如果需要用第二個方法,可以參考下面的做法。

首先,將圖片上載到 Google Drive。接着就用 script 把這些圖片檔變成 Blob。以下的 function 示範了如何抽取在 EMAIL_SIGNATURE_IMAGE_FOLDER_ID 入面的檔案的 Blob。Folder ID 其實就是用網頁版 Google Drive 開啟 folder 後在網址見到的一串 ID。

function extractEmailSignatureBlobs() {
   var result = {}
@@ -46,8 +53,8 @@
     sheet.getRange(rowIndex, 5).setValue(new Date());
   }
 }

基本上就是一個 loop 逐行檢查,如果需要發電郵就用 GmailApp 製造郵件放到自己 Gmail 的草稿箱。

執行完成後會看到 Sent By 和 Sent At 兩欄經已被填妥。下次再執行時程式會檢查 Sent At 是否為空,那就不會重覆發送郵件。

執行完成後的得獎名單

完整程式碼

Google Apps Script 程式碼、HTML 和試算表內容已經放到 Gist 上。

下一步

之後可以考慮加入自訂選單。有了自訂選單就不用每次都要進入 Script editor 才能執行程式。

結語

上一篇和這一篇分別介紹了 Google Apps Script 操作 Spreadsheet、Gmail、Drive 和 HTML template 功能。你可以結合一起使用,提昇工作效率。例如 Spreadsheet 內容由 Form 形式輸入、使用 Maps Service 做 Geocoding 等等。此外,Google Apps Script 的 function 也可以當 spreadsheet 的 function 使用。如果你常常使用 Google Drive 的辦公室軟件功能的話 Google Apps Script 實在不容錯過。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2019/07/android-app-icon-specification/index.html b/2019/07/android-app-icon-specification/index.html index 8aa2b06a..b9ab2442 100644 --- a/2019/07/android-app-icon-specification/index.html +++ b/2019/07/android-app-icon-specification/index.html @@ -1,5 +1,5 @@ Android App Icon 規格 | EricLog -

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon:

  • Launcher icon
  • Launcher icon(圓形)
  • Adaptive launcher icon
  • Google Play icon

如果想簡單地做出全部 icon 的話,可以用 Android Studio 內置的 Image Asset Studio。但如果想追求完美的話,還是自行準備圖片比較好。這篇文章整理了不同 icon 的基本規格,寫的時候盡量考慮到設計師。如果有需要的話可以分享給設計師同事參考。

Android Studio 的 Image Asset Studio

背景知識

屏幕密度

因為 Android 設備有太多款,所以 Android 在處理不同屏幕方面比起 iOS 更細緻:由最基本的屏幕密度到控制在不同屏幕尺寸下的界面排版都比 iOS 有更多設置。如果是設計 app icon 的話,主要留意的是屏幕密度。iOS 目前有三款密度:1x、2x (@2x) 和 3x (@3x)﹔而 Android 的密度就有 1x (MDPI)、1.5x (HDPI)、2x (XHDPI)、3x (XXHDPI) 和 4x (XXXHDPI)。建議大家使用向量圖準備原檔,並且以 1x 的尺寸準備圖片。在匯出圖片時才一次過匯出全部密度的 PNG 圖片。

在 Android,一般會使用 dp 這個長度單位來設計 UI。如果不清楚 dp 是甚麼意思的話,可以參考 Material Design 的 Pixel density 部分。但在高密度的屏幕下,如果要把圖案顯示到和低密度屏幕一樣大小(把間尺放在屏幕上量度那個圖案)而又不模糊的話,是要用更多的像素 (pixel, px)。所以就發明了 dp 這個單位來描述尺寸,即使在不同密度的屏幕下 dp 都是一樣。簡單來說,在 1x 的情況下,1dp = 1px。你要在 1x 的情景下繪畫你的圖案,然後按照上一段的 1.5x、2x、3x……倍數匯出成不同尺寸的圖片。

舉個例子:如果要準備一張 32 × 32dp 的圖片,你要匯出以下的 PNG 檔案:

密度放大倍數PNG 圖片闊度及高度 (px)
MDPI1x32
HDPI1.5x48
XHDPI2x64
XXHDPI3x96
XXXHDPI4x128

PNG 檔案名稱需要一致,只可以用小楷英文字母、底線 (_) 和數字(數字不可在作為名稱開首),並將圖片放到對應的資料夾內(例如 drawable-hdpi)。

主畫面

Android 的主畫面是叫作 launcher,它的功能除了主畫面外,還包括讓用戶選擇要啟動的 app(app 列表)。Launcher 本身是一個 app。和其他 app 一樣,用戶可以在 Google Play 下載其他 launcher 來替代預設的 launcher。不同的 launcher 在顯示 app icon 方面都有不同的效果。簡單來說,app icon 顯示效果取決於 Android 版本和用戶所選用的 launcher。

Launcher Icon

這個是最基本的 app icon,就是 Android 最先出現的 app icon 形式。稱為 launcher icon 就是因為它主要在 launcher 內顯示。(其實也會在設定頁的應用程式清單中看到)

iOS 不同,Android 的 launcher icon 是沒有規定形狀。在 Android 4 時代的指引(鏡像網站)是鼓勵 icon 的形狀要有特色,不要套上圓角方形之類的遮色片 (clipping mask) 或完全填滿全部像素來侷限 icon 設計。但太多人受到 iOS 的影響,都把 launcher icon 做成像 iOS 般的圓角方形。有些甚至用盡 launcher icon 尺寸,沒有按規格在四邊留足夠空間來加上陰影。結果不同款色的 launcher icon 放在同一個 launcher 時視覺完全不協調。一些 Android 設備品牌為了解決這個問題,在它們的預設 launcher 會做到像 iOS 般為所有 app icon 加上圓角方形的遮色片。亦有一些 launcher 會為所有 app icon 加上托底,令原先不同形狀的 app icon 看上來都統一。加上 Google Play Store 上架無需人工審批,即使 launcher icon 設計不符指引也沒有問題。結果直到現在看到的 app icon 設計都是不統一。

在後期的 Android 版本為了解決這個問題先後出了好幾款的 app icon 形式(就是之後介紹那些)。但這個 launcher icon 形式仍是必須的。

規格方面,尺寸是 48 × 48dp。有關設計原則、格線、形狀、高光、陰影之類可以直接查閱 Material Design 網站。Illustrator 範本可在舊版 Material Design 網站的 Resources – Sticker sheets & icons(Product icons 部分)下載,那些 AI 檔案已經包含了格線、基本形狀、高光、陰影的示範。

Launcher icon 連格線示範

不過要留意一點:這個範本的尺寸是 192 × 192pt,而不是 48 × 48px。因為 launcher icon 指引建議大家放大 400% 來設計(但需要以 4pt 的格線來繪制),然後在匯出時是縮小成不同的 PNG 檔案而不是像平時般放大圖片。

完成設計後按以下的規格匯出:

闊度及高度 (px)密度放大倍數
48MDPI0.25x
72HDPI0.375x
96XHDPI0.5x
144XXHDPI0.75x
192XXXHDPI1x

把匯出的 PNG 檔案按以下的目錄結構放好:

.
+

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon:

  • Launcher icon
  • Launcher icon(圓形)
  • Adaptive launcher icon
  • Google Play icon

如果想簡單地做出全部 icon 的話,可以用 Android Studio 內置的 Image Asset Studio。但如果想追求完美的話,還是自行準備圖片比較好。這篇文章整理了不同 icon 的基本規格,寫的時候盡量考慮到設計師。如果有需要的話可以分享給設計師同事參考。

Android Studio 的 Image Asset Studio

背景知識

屏幕密度

因為 Android 設備有太多款,所以 Android 在處理不同屏幕方面比起 iOS 更細緻:由最基本的屏幕密度到控制在不同屏幕尺寸下的界面排版都比 iOS 有更多設置。如果是設計 app icon 的話,主要留意的是屏幕密度。iOS 目前有三款密度:1x、2x (@2x) 和 3x (@3x)﹔而 Android 的密度就有 1x (MDPI)、1.5x (HDPI)、2x (XHDPI)、3x (XXHDPI) 和 4x (XXXHDPI)。建議大家使用向量圖準備原檔,並且以 1x 的尺寸準備圖片。在匯出圖片時才一次過匯出全部密度的 PNG 圖片。

在 Android,一般會使用 dp 這個長度單位來設計 UI。如果不清楚 dp 是甚麼意思的話,可以參考 Material Design 的 Pixel density 部分。但在高密度的屏幕下,如果要把圖案顯示到和低密度屏幕一樣大小(把間尺放在屏幕上量度那個圖案)而又不模糊的話,是要用更多的像素 (pixel, px)。所以就發明了 dp 這個單位來描述尺寸,即使在不同密度的屏幕下 dp 都是一樣。簡單來說,在 1x 的情況下,1dp = 1px。你要在 1x 的情景下繪畫你的圖案,然後按照上一段的 1.5x、2x、3x……倍數匯出成不同尺寸的圖片。

舉個例子:如果要準備一張 32 × 32dp 的圖片,你要匯出以下的 PNG 檔案:

密度放大倍數PNG 圖片闊度及高度 (px)
MDPI1x32
HDPI1.5x48
XHDPI2x64
XXHDPI3x96
XXXHDPI4x128

PNG 檔案名稱需要一致,只可以用小楷英文字母、底線 (_) 和數字(數字不可在作為名稱開首),並將圖片放到對應的資料夾內(例如 drawable-hdpi)。

主畫面

Android 的主畫面是叫作 launcher,它的功能除了主畫面外,還包括讓用戶選擇要啟動的 app(app 列表)。Launcher 本身是一個 app。和其他 app 一樣,用戶可以在 Google Play 下載其他 launcher 來替代預設的 launcher。不同的 launcher 在顯示 app icon 方面都有不同的效果。簡單來說,app icon 顯示效果取決於 Android 版本和用戶所選用的 launcher。

Launcher Icon

這個是最基本的 app icon,就是 Android 最先出現的 app icon 形式。稱為 launcher icon 就是因為它主要在 launcher 內顯示。(其實也會在設定頁的應用程式清單中看到)

iOS 不同,Android 的 launcher icon 是沒有規定形狀。在 Android 4 時代的指引(鏡像網站)是鼓勵 icon 的形狀要有特色,不要套上圓角方形之類的遮色片 (clipping mask) 或完全填滿全部像素來侷限 icon 設計。但太多人受到 iOS 的影響,都把 launcher icon 做成像 iOS 般的圓角方形。有些甚至用盡 launcher icon 尺寸,沒有按規格在四邊留足夠空間來加上陰影。結果不同款色的 launcher icon 放在同一個 launcher 時視覺完全不協調。一些 Android 設備品牌為了解決這個問題,在它們的預設 launcher 會做到像 iOS 般為所有 app icon 加上圓角方形的遮色片。亦有一些 launcher 會為所有 app icon 加上托底,令原先不同形狀的 app icon 看上來都統一。加上 Google Play Store 上架無需人工審批,即使 launcher icon 設計不符指引也沒有問題。結果直到現在看到的 app icon 設計都是不統一。

在後期的 Android 版本為了解決這個問題先後出了好幾款的 app icon 形式(就是之後介紹那些)。但這個 launcher icon 形式仍是必須的。

規格方面,尺寸是 48 × 48dp。有關設計原則、格線、形狀、高光、陰影之類可以直接查閱 Material Design 網站。Illustrator 範本可在舊版 Material Design 網站的 Resources – Sticker sheets & icons(Product icons 部分)下載,那些 AI 檔案已經包含了格線、基本形狀、高光、陰影的示範。

Launcher icon 連格線示範

不過要留意一點:這個範本的尺寸是 192 × 192pt,而不是 48 × 48px。因為 launcher icon 指引建議大家放大 400% 來設計(但需要以 4pt 的格線來繪制),然後在匯出時是縮小成不同的 PNG 檔案而不是像平時般放大圖片。

完成設計後按以下的規格匯出:

闊度及高度 (px)密度放大倍數
48MDPI0.25x
72HDPI0.375x
96XHDPI0.5x
144XXHDPI0.75x
192XXXHDPI1x

把匯出的 PNG 檔案按以下的目錄結構放好:

.
 ├── mipmap-hdpi
 │   └── ic_launcher.png (72 × 72px)
 ├── mipmap-mdpi
@@ -80,8 +80,8 @@
     <!-- 略 -->
 </application>
 

其實這個 banner 有個潛規則:不可以使用黑色、深灰色之類的背景顏色。因為 Android TV 的 launcher 背景色是深色為主,如果 banner 背景顏色都是用深色的話就難以從背景色中分離出來。Banner 背景顏色太深色的話 Google 是不會讓你的 app 上架。(Android TV 新 app 上架和 iOS App Store 一樣都需要送檢,但之後的更新就不用送檢。)如果想了解更多的話可以參考我之前上架 TrainBoard 的例子。

順帶一提,Android TV design guidelines 提到在電視屏幕上不應使用純白色 (#FFFFFF)。因為太鮮色會刺眼。如果要用白色的話應該使用淺灰色 (#EEEEEE)。

參考及閱讀更多

comments powered by Disqus
comments powered by Disqus + PaperMod
\ No newline at end of file diff --git a/2020/02/firebase-cloud-messaging/index.html b/2020/02/firebase-cloud-messaging/index.html index 95818751..739e9e3d 100644 --- a/2020/02/firebase-cloud-messaging/index.html +++ b/2020/02/firebase-cloud-messaging/index.html @@ -1,5 +1,8 @@ Firebase Cloud Messaging | EricLog -

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。

如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。

FCM 的 push 有分兩種:Notification message 和 Data message。Notification message 就是那些在發送時預先指明式樣的 push。即是標題、內文、notification channel 之類的 push。這種 push 可以用 Firebase 的 Notifications composer 造出來,完全不用寫 code。如果裝置收到 push 時你的 app 是在 foreground 的話就會觸發 onMessageReceived callback 讓你自己處理;在 background 時就直接後會由 Firebase SDK 直接生成 Android 看到的系統通知(即是在 system tray 看到的 NotificationCompat)而不會觸發 onMessageReceived callback。如果要在 Firebase API 發送 push 的話 request body 大概是這樣:

{
+

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。

如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。

FCM 的 push 有分兩種:Notification message 和 Data message。Notification message 就是那些在發送時預先指明式樣的 push。即是標題、內文、notification channel 之類的 push。這種 push 可以用 Firebase 的 Notifications composer 造出來,完全不用寫 code。如果裝置收到 push 時你的 app 是在 foreground 的話就會觸發 onMessageReceived callback 讓你自己處理;在 background 時就直接後會由 Firebase SDK 直接生成 Android 看到的系統通知(即是在 system tray 看到的 NotificationCompat)而不會觸發 onMessageReceived callback。如果要在 Firebase API 發送 push 的話 request body 大概是這樣:

{
   "message": {
     "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
     "notification": {
@@ -30,8 +33,8 @@
   }
 }

跟之前兩款不同的地方是它既有 notification 又有 data 兩個 object。在這種情況下 app 是在 foreground 的話會觸發 onMessageReceived callback;在 background 的話就像 Notification message 般直接在裝置的 system tray 顯示 notification。當用戶按下 notification 開 app 時那些 payload 就會由 intent extra 送到預設開 app 的 Activity 內。

所以如果你想不論何時都能讓 app 即時處理到 push 的話,那就不要附帶 notification object。這樣就變成 Notification message,不論 foreground 還是 background 都可以被 app 收到。

用了 Notification message 但 onMessageReceived 沒有被 call

有時即使裝置已經收到 Notification message 的 push 但仍然收不到 onMessageReceived callback,在 Logcat 可以看到這段 log:

2020-02-18 11:36:41.862 3369-3369/? W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=net.swiftzer.metroride (has extras) }
 

這個情況在 Firebase 的文檔應該沒有提及,但 Stack Overflow 有人回答過相同問題。沒有 callback 但出現以上的 log 原因是因為這就是 Android framework 的設計:如果用戶主動 kill app 的話即使收到 FCM push 都不會啟動你的 app,直至用戶再次主動開啟你的 app 才會解除限制,重新開機也不可重設,一定要主動開 app 才會解除。用戶主動 kill app 是指用戶在設定頁找到你的 app 再按強制停止,如果是因為系統不夠 RAM 而 kill app 就不算。

但這個回答有提過這個機制在某些品牌會變成用戶在最近使用 app 的畫面掃走 app 時都會有這個效果。所以比較穏妥的做法是如果那個 notification 不需要 app 去特別處理(例如 payload 的話)都附帶 notification object。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2020/03/firebase-crashlyticsorgidexception/index.html b/2020/03/firebase-crashlyticsorgidexception/index.html index 20fbfac7..4e998e8f 100644 --- a/2020/03/firebase-crashlyticsorgidexception/index.html +++ b/2020/03/firebase-crashlyticsorgidexception/index.html @@ -1,5 +1,16 @@ Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法 | EricLog -

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤:

java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id
+

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤:

java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id
 > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id
   > Could not fetch Crashlytics Org Id
     > Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811
@@ -7,8 +18,8 @@
     mappingFileUploadEnabled false
 }
 

起初以為是那個 google-services.json 放錯位置,但我檢查過位置是正確。之後我找了 Firebase 支援,他們最後回覆說原因是我沒有在 production 的 Firebase project 交過一次 crash report,所以 production Firebase project 的 Crashlytics Org Id 就沒有產生出來,導致交不了 ProGuard/R8 mapping file。只不過他們的文檔沒有寫明一定要 crash 一次才算正式完成安裝 Crashlytics。

成功看到 deobfuscate 過的 crash report
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2020/03/moshi-r8-parameter-type-is-null/index.html b/2020/03/moshi-r8-parameter-type-is-null/index.html index e5c6d8a2..77b4899f 100644 --- a/2020/03/moshi-r8-parameter-type-is-null/index.html +++ b/2020/03/moshi-r8-parameter-type-is-null/index.html @@ -1,5 +1,5 @@ Moshi Kotlin Codegen + R8 出現 parameter type is null | EricLog -

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。

parameter type is null
java.lang.NoSuchMethodException: parameter type is null
+

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。

parameter type is null
java.lang.NoSuchMethodException: parameter type is null
     at java.lang.Class.getConstructor0(Class.java:2322)
     at java.lang.Class.getDeclaredConstructor(Class.java:2166)
     at net.swiftzer.metroride.remote.mtrupdate.status.LineStatusResponse_LineStatusJsonAdapter.a(LineStatusResponse_LineStatusJsonAdapter.kt:70)
@@ -49,8 +49,8 @@
     }
 }
 

效果:

改了 R8 版本後 Moshi 能正常 deserialize JSON

參考

現在(2020 年 4 月 26 日)的 R8 版本應該沒有這個問題。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2020/04/kotlin-annotation-processor/index.html b/2020/04/kotlin-annotation-processor/index.html index caa98556..7363524d 100644 --- a/2020/04/kotlin-annotation-processor/index.html +++ b/2020/04/kotlin-annotation-processor/index.html @@ -1,5 +1,5 @@ Kotlin Annotation Processor | EricLog -

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。

自己做一個功能不多的 annotation processor 其實都不太難。難的地方是 debug 時不能像平常般加 breakpoint,加上我們要生成 Kotlin class 所以跟平常找到的教學會有輕微出入(因為大部分教學都是討論生成 Java class)。

這篇文章會示範做一個生成 feature flag 的 class。這個 feature flag 其實就是一個 interface,入面有不同的 method,一個 method 代表一個 feature flag,它們都是會回傳 boolean。把它做成 interface 而不直接定義一堆 boolean constant 的原因是方便寫 test case。我們可以 mock 那個 interface 就能測試那個 flag 是開和關的情境。下面是一個 interface 例子:

package com.example.annotation
+

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。

自己做一個功能不多的 annotation processor 其實都不太難。難的地方是 debug 時不能像平常般加 breakpoint,加上我們要生成 Kotlin class 所以跟平常找到的教學會有輕微出入(因為大部分教學都是討論生成 Java class)。

這篇文章會示範做一個生成 feature flag 的 class。這個 feature flag 其實就是一個 interface,入面有不同的 method,一個 method 代表一個 feature flag,它們都是會回傳 boolean。把它做成 interface 而不直接定義一堆 boolean constant 的原因是方便寫 test case。我們可以 mock 那個 interface 就能測試那個 flag 是開和關的情境。下面是一個 interface 例子:

package com.example.annotation
 
 import com.example.annotation.annotation.FeatureFlag
 import com.example.annotation.annotation.FeatureFlagGroup
@@ -137,8 +137,8 @@
         }
         builder
     }.build()

Sample Module

現在可以來看看它的效果。完整的 code 可以在 Gist 找到。

測試用的 Main、interface class 和生成的 class

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2020/08/conventional-commits-commitlint/index.html b/2020/08/conventional-commits-commitlint/index.html index 7264b756..dbe1d9be 100644 --- a/2020/08/conventional-commits-commitlint/index.html +++ b/2020/08/conventional-commits-commitlint/index.html @@ -1,12 +1,12 @@ Conventional Commits 和 commitlint | EricLog -

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。

一般的 Git client 因為界面空間和方便檢索,在顯示紀錄時都是顯示 commit message 的第一行。所以第一行是最重要。

<type>[optional scope]: <description>
+

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。

一般的 Git client 因為界面空間和方便檢索,在顯示紀錄時都是顯示 commit message 的第一行。所以第一行是最重要。

<type>[optional scope]: <description>
 
 [optional body]
 
 [optional footer(s)]
 

Conventional Commits 亦是這樣處理。上面是 Conventional Commits 的 commit message 結構。第一行是 header。Header 分為三部分:type、scope 和 subject。Type 就是分類,例子有 fixfeat,分別表示修正和新功能。如果按 Angular 的約定,還有 buildcidocsperfrefactorstyletest 可供選擇。值得一提是 chore 已經被刪走,原因是因為太多人濫用。緊接 type 的就是 scope,它是用來提供除 type 之外額外的脈絡資訊,例如是用來注明改動和那個組件或功能相關。而 header 最重要的部分 subject 就是為這個 commit 提供一個簡短的總結。

因為 Git client 顯示 commit message 第一行的空間有限,如果 subject 空間有限而未能完全表達的話(例如是講解變動的原因),可以寫在 body 部分。Body 可以多於一段,用空行分隔。

而 footer 就是用來放 issue tracker 的 issue ID、pull request 或 merge request ID、審核者、重大變更之類,一行一個詮釋。

除了規範格式方便其他人閱讀 Git log 以外,Conventional Commits 的另一個主要作用是用來控制版本號碼的變更。具體來說就是跟 Semantic Versioning (SemVer) 連動。如果 Git log 有 fix 的 commit 就對應 SemVer 的 PATCH release;feat 就對應 MINOR release;BREAKING CHANGE 就對應 MAJOR release。

另外,可以配合對應 Conventional Commits 的工具自動生成 CHANGELOG,毋須再人手整理,減少遺漏項目的機會。

Conventional Commits 還有其他的規範,我不花時間逐一說明。可以到他們的網站閱讀,網頁已被翻譯成不同的語言。


有了 Conventional Commits 這個規範都未能完全解決 commit message 混亂的問題,因為總有人不按規範寫 commit message。所以就有 Conventional Commits 的 linter,例好 commitsarcommitlint。如果用 commitlint 的話,它有一個 @commitlint/config-conventional 的設定讓你做 lint rule 基底。它是按照 Conventional Commits 規範來設定,但因為規範要適用於不同的 project,所以不能寫得太嚴格。如果想要更細緻的規範,commitlint 提供了額外的 rule 可供選用。

為方便大家了解 @commitlint/config-conventional 的 rule,下面我準備了一張 cheat sheet(另有 PDF 檔案)。希望這張 cheat sheet 可以令你更容易向你的團隊推廣 Conventional Commits。

Cheat sheet
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2020/08/javax-measure/index.html b/2020/08/javax-measure/index.html index 09225089..a99095ac 100644 --- a/2020/08/javax-measure/index.html +++ b/2020/08/javax-measure/index.html @@ -1,5 +1,5 @@ Java 量度單位 (JSR 363 Units of Measurement API) | EricLog -

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 intfloatdouble 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。

如果是 Android 的話可以用 android.icu.util.Measure,但要 API level 24 (Android 7.0) 或以上才可使用,而且不包括單位轉換功能。

如果想在 Android API level 24 以前的裝置用到的話,可以考慮用 JSR 363 (Units of Measurement API)。它提供了一套 API 來表示量度單位和數值,而且包含單位轉換和格式化。

如果要用這個 library 的話,是需要這兩個 artifact:

implementation "tech.units:indriya:2.0.2"
+

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 intfloatdouble 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。

如果是 Android 的話可以用 android.icu.util.Measure,但要 API level 24 (Android 7.0) 或以上才可使用,而且不包括單位轉換功能。

如果想在 Android API level 24 以前的裝置用到的話,可以考慮用 JSR 363 (Units of Measurement API)。它提供了一套 API 來表示量度單位和數值,而且包含單位轉換和格式化。

如果要用這個 library 的話,是需要這兩個 artifact:

implementation "tech.units:indriya:2.0.2"
 implementation("systems.uom:systems-common:2.0.2") {
     exclude group: 'jakarta.annotation', module: 'jakarta.annotation-api'
 }

tech.units:indriya 是 JSR 363 的 reference implementation。它其實是用了 javax.measure:unit-api 所定義的 interface 的一個實作。而 systems.uom:systems-common 就提供了一些主流的單位的定義,例如 SI(國際單位制)、Imperial(英制)和 USCustomary(美制)。

要去除 jakarta.annotation-api 是因為 Android Gradle plugin desugaring 時會加入和 jakarta.annotation 同名的 class,例如 javax.annotation.Generated

下面是一個由公里轉成哩的例子:

import org.junit.Assert.assertEquals
@@ -26,8 +26,8 @@
     val kg = twoCatty.to(SI.KILOGRAM)
     assertEquals(1.209580, kg.value.toDouble(), 0.000001)
 }

值得一提,斤在不同地方有不同定義。中國的一斤是等於 0.5 公斤;台灣的一斤是等於 0.6 公斤。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/index.html b/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/index.html index 10089fde..37a39690 100644 --- a/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/index.html +++ b/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/index.html @@ -1,5 +1,5 @@ Jetpack DataStore 搭配 kotlinx.serialization Protobuf | EricLog -

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法:

  1. Preferences DataStore:像 SharedPreferences 般以 key 存取資料,可以隨時加新 key,而且沒有特別的 type checking 處理,全靠讀取時指明 value 的 data type。
  2. Proto DataStore:用 Protobuf 來儲存資料,存取時候都是直接經 Java/Kotlin class,所以和 SharedPreferences 及 Preferences DataStore 相比是 type safe。

既然要用 DataStore,那就當然要用 Proto DataStore。如果有看過 Codelab 的話,它都會叫大家用 com.google.protobuf:protoccom.google.protobuf:protobuf-javalite 把 proto 檔案生成對應的 Java class。就像處理 JSON 要找 Gson 之類的 library 做 serialization/deserialization 一樣,不過 Protobuf 的用法是先定義好一個 proto 檔(即是 schema),然後把這個檔案交給 protoc compiler 生成不同程式語言的 entity class,然後會有另一個 library 做 entity class 和 protobuf serialization/deserialization。但其實 DataStore 的 API 設計並沒有硬性規定要用 Google 的 Protobuf library,甚至無規定用 Protobuf 格式。

DataStore 的 API 就是提供一個 Kotlin Coroutine/Flow 的方式讀寫 object(Proto DataStore 的話)。這樣就可以逼大家把 I/O 動作放去其他 thread 執行(預設是用 Dispatchers.IO),又可以用 Flow observe 改動。相比起 SharedPreferencesSharedPreferences 的 API 大部分都是 synchronous,調用起來又很快,放在 UI thread 好像問題不大。但有時候改動 data 時用 apply() 後馬上讀取可能會讀不到最新值,用 commit() 又因為用 SharedPreferences 時未有考慮到寫入是 asynchronous 所以 linter 又出警告。所以 DataStore API 設計上就索性改用 Kotlin Coroutine/Flow。而用 Protobuf 儲資料是因為它既 type safe 又比 SharedPreferences 所用的 XML 細小。DataStore 的 Protobuf 存放位置跟 SharedPreferences 有點不同,它是放在 files/datastore 目錄內,而不是 shared_prefs 內。

DataStore 存放位置

搭配 kotlinx.serialization

⚠️ 註:這篇文章是以 Jetpack DataStore 1.0.0-alpha03 來寫的。

如果想改用 kotlinx.serialization 來處理 Protobuf 的話,首先要準備 data class。Data class 要有 @Serializable,property 最好要有 @ProtoNumber,以便日後 data class 增減 property 後 Protobuf 能夠相容。

import kotlinx.serialization.Serializable
+

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法:

  1. Preferences DataStore:像 SharedPreferences 般以 key 存取資料,可以隨時加新 key,而且沒有特別的 type checking 處理,全靠讀取時指明 value 的 data type。
  2. Proto DataStore:用 Protobuf 來儲存資料,存取時候都是直接經 Java/Kotlin class,所以和 SharedPreferences 及 Preferences DataStore 相比是 type safe。

既然要用 DataStore,那就當然要用 Proto DataStore。如果有看過 Codelab 的話,它都會叫大家用 com.google.protobuf:protoccom.google.protobuf:protobuf-javalite 把 proto 檔案生成對應的 Java class。就像處理 JSON 要找 Gson 之類的 library 做 serialization/deserialization 一樣,不過 Protobuf 的用法是先定義好一個 proto 檔(即是 schema),然後把這個檔案交給 protoc compiler 生成不同程式語言的 entity class,然後會有另一個 library 做 entity class 和 protobuf serialization/deserialization。但其實 DataStore 的 API 設計並沒有硬性規定要用 Google 的 Protobuf library,甚至無規定用 Protobuf 格式。

DataStore 的 API 就是提供一個 Kotlin Coroutine/Flow 的方式讀寫 object(Proto DataStore 的話)。這樣就可以逼大家把 I/O 動作放去其他 thread 執行(預設是用 Dispatchers.IO),又可以用 Flow observe 改動。相比起 SharedPreferencesSharedPreferences 的 API 大部分都是 synchronous,調用起來又很快,放在 UI thread 好像問題不大。但有時候改動 data 時用 apply() 後馬上讀取可能會讀不到最新值,用 commit() 又因為用 SharedPreferences 時未有考慮到寫入是 asynchronous 所以 linter 又出警告。所以 DataStore API 設計上就索性改用 Kotlin Coroutine/Flow。而用 Protobuf 儲資料是因為它既 type safe 又比 SharedPreferences 所用的 XML 細小。DataStore 的 Protobuf 存放位置跟 SharedPreferences 有點不同,它是放在 files/datastore 目錄內,而不是 shared_prefs 內。

DataStore 存放位置

搭配 kotlinx.serialization

⚠️ 註:這篇文章是以 Jetpack DataStore 1.0.0-alpha03 來寫的。

如果想改用 kotlinx.serialization 來處理 Protobuf 的話,首先要準備 data class。Data class 要有 @Serializable,property 最好要有 @ProtoNumber,以便日後 data class 增減 property 後 Protobuf 能夠相容。

import kotlinx.serialization.Serializable
 import kotlinx.serialization.protobuf.ProtoNumber
 
 @Serializable
@@ -56,8 +56,8 @@
     // config is the current value
     config.copy(version = "2.0")
 }

用不用 DataStore 好

用 Jetpack DataStore 前期準備好像比 SharedPreferences 複雜(因為要準備 .proto 檔 / data class 又要寫一個 Serializer),但就多了 type safe 特性。如果你會把 JSON string 塞入 SharedPreferences 的話,用 DataStore 會比 SharedPreferences 好。因為 SharedPreferences 背後是儲存在 internal storage 的 XML 檔。如果 value 是 JSON 的話,那就是要 serialize/deserialize 兩次(用 SharedPreferencesgetString deserialize 一次,之後再用 Gson 之類又再 deserialize 一次)。改用 DataStore 就直接由 Protobuf deserialize 一次就行。所以如果是 nested object 的話用 DataStore 其實是很好的。至於一般 key value 的話要看你想不想轉用。

如果你有用 AndroidX Preference 的話,要留意需要重寫設定頁。因為 PreferenceFragmentCompat 本來就是配 SharedPreferences 來用,如果想自訂儲存方式的話,本來是可以用 PreferenceDataStore,但那些 method 都是 synchronous,所以現實上很難改到。不過大部分人都是自製設定頁,所以這部分問題不大。最大問題應該是要把全部本來用 SharedPreferences 的地方都要改做 asynchronous。如果當初寫的時候沒有把讀寫 SharedPreferences 的地方都有考慮到 asynchronous 的話那個改動就會超大,除非把 DataStore 的 Flow 和 Coroutine 都強行變成 runBlocking

至於 kotlinx.serialization 方面,你可能會留意到我們從來沒有寫過 .proto 檔。這是因為 kotlinx.serialization 是靠 Kotlin class 和 annotation 定義 schema,有別於其他 library 要由 .proto 生成 Java/Kotlin class 才能用。如果那個 Protobuf 只會在 Android app 用到,那問題不大。但如果要處理的 Protobuf 是跨平台(例如 backend 拍板 schema,其他地方例如 Android、iOS、Web 要跟那個 schema 的話),用 kotlinx.serialization 會比較尷尬。因為人家給你的 .proto 檔不能直接用,要自己對住它用 Kotlin 寫一次 data class。但寫的時候不能保證你寫的跟人家的 .proto 定義一致。如果不能 serialize/deserialize 又要額外花時間 debug。因為 kotlinx.serialization 定位是以 Kotlin 為中心,只需要 backend frontend 共用那個 Kotlin file 就能解決問題,完全不需要交換 .proto 檔。不過如果你有跨平台的考量需要交換 .proto 檔的話,那可能用 Google 的 Protobuf library 或者 Wire 之類比較合適。但如果情況是 backend 用 JSON,想在 Android 用 DataStore Proto 儲存的話(可能是一些零碎需要 offline cache 的內容),直接用 kotlinx.serialization 或許比較方便,因為可以共用同一個 Kotlin data class,kotlinx.serialization 的 annotation 又是跨格式共用的。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021-ithome-ironman/index.html b/2021-ithome-ironman/index.html index 9da70df0..fd4c5414 100644 --- a/2021-ithome-ironman/index.html +++ b/2021-ithome-ironman/index.html @@ -1,6 +1,166 @@ 2021 iThome 鐵人賽「寫一個列車抵站時間 Android App」文章目錄 | EricLog -

2021 iThome 鐵人賽「寫一個列車抵站時間 Android App」文章目錄

以下是我參加 2021 iThome 鐵人賽 所寫的系列文章「寫一個列車抵站時間 Android App」的目錄。原文在 iThome 首次發表,在完賽後轉貼到這裏。

文章示範的 code 可以在 GitHub repo 找到。


  1. Intro
    簡單的開場白,介紹將會提及的內容。
  2. Architecture
    Architecture Components (MVVM)、Modularization 和這次示範 app 所用的 architecture。
  3. Endpoint
    示範 app 會用到的 API endpoint 和 API response 對應的 data class。
  4. Deserialization
    比較 Gson、Moshi、Kotlin Serialization,並為上篇準備的 data class 加上 Kotlin Serialization 的 annotation。
  5. Dependency injection
    為 project 加入 Dagger Hilt。
  6. HTTP Client
    Ktor client 簡介、基本用法及把 Ktor client 加進 Dagger dependency graph。當中提及 @Inject@InstallIn@Singleton@BindsOptionalOf 用法。
  7. Data layer implementation (1)
    車站及路綫 enum、domain layer repository interface、實作接駁 API endpoint、Dagger @Binds 用法及 Ktor client 跟 Retrofit 比較。
  8. Data layer implementation (2)
    API response data class 轉換到 domain layer data class 的 mapper。
  9. Date & time
    討論為甚麼要用 Java Time 而不是用 JDK 傳統的 CalendarDate 的原因、為 project 加入 desugering library 及介紹 Time Zone Database。
  10. Data layer testing (1)
    JUnit 4 test、MockK 及 Strikt 基本用法。
  11. Data layer testing (2)
    用 Strikt 為 object 每個 property 做 assertion。
  12. Data layer testing (3)
    Ktor client 搭配 logback-android 在 unit test 時出現錯誤的解決方法、運用 Ktor mock engine 模擬 server response。
  13. Data layer testing (4)
    運用上篇的 Ktor mock engine 測試觸發 HTTP request 的 code 和經過 Kotlin Serialization deserialize 後的 response object 是否合符預期,另加 Strikt 自定義的 assertion。
  14. Flipper
    安裝 Flipper(只在 debug build type 才安裝)、Dagger Hilt 的 @HiltAndroidApp@ApplicationContext 用法、Dagger @Qualifier@BindsOptionalOf 用法、OkHttp client 加入 interceptor。
  15. Domain layer implementation
    實作 use case。
  16. Domain layer testing
    Use case 的 unit test。
  17. Navigation (1)
    安裝 Navigation component,準備將會用到的 Fragment 和 layout XML 檔案、簡單介紹 Dagger Hilt 的 @AndroidEntryPoint 及在 Fragment 使用 data binding 時要注意的地方。
  18. Navigation (2)
    定義 Navigation graph 並設定轉頁的參數、設定 MainActivity 以顯示 navigation graph 的頁面及使用轉頁參數時要注意的地方。
  19. Station list screen (1)
    實作車站列表頁介面,使用了 RecyclerViewListAdapterDiffUtil、data binding,並示範了 Dagger Hilt 的 @ActivityScoped 用法。
  20. Station list screen (2)
    實作車站列表頁的 ViewModel,示範了 Dagger Hilt 的 @HiltViewModel、Kotlin Flow 的 ChannelStateFlow/MutableStateFlowcombinerepeatOnLifecycle 用法和 Navigation component 防止用戶快速連按轉頁按鈕導致 crash 的方法。
  21. ETA screen (1)
    介紹抵站時間頁的流程,實作頁面基本的 layout XML、RecyclerViewViewModelonBackPressedDispatcher,另外亦示範了 Kotlin Sequence
  22. Whistle proxy
    安裝 Whistle proxy、Whistle proxy 基本用法、在 Android 裝置安裝根證書、設定 network security configuration 及介紹 Proxy Toggle。
  23. ETA screen (2)
    SavedStateHandle 用法及以 Kotlin Flow 控制各式錯誤介面顯示。
  24. ETA screen (3)
    自動更新班次,介紹 Kotlin Coroutine 的 CoroutineScopeJobdelay 用法並使用 Java Time 的 Clock 取得當前時間。
  25. ETA screen (4)
    顯示錯誤 banner,介紹 Kotlin Coroutine 的 scan (runningFold) 用法,順帶講解 Kotlin Collection 的 foldreduce
  26. Station list screen testing
    安裝 Robolectric,為引用了 Android Context 的 class 寫 unit test;針對 Flipper 在 unit test 時報錯的解決方法;替換 Dispatchers.Main 及運用 Turbine 測試 Flow。
  27. ETA screen testing (1)
    示範 JUnit 4 parameterized test;介紹 cold flow (StateFlow) 跟 hot flow (SharedFlow) 的分別。
  28. ETA screen testing (2)
    運用 ThreeTen-Extra 提供的 MutableClock 及 Kotlin Coroutine 的 DelayController 改變時間並測試自動更新班次的邏輯。
  29. Leftover topics
    Two-way data binding、RecyclerView 局部更新、unidirectional data flow、instrumentation test 及 Coroutine dispatcher。
  30. Wrapping up
    參賽總結。
comments powered by Disqus
© 2024 EricLog +

2021 iThome 鐵人賽「寫一個列車抵站時間 Android App」文章目錄

以下是我參加 2021 iThome 鐵人賽 所寫的系列文章「寫一個列車抵站時間 Android App」的目錄。原文在 iThome 首次發表,在完賽後轉貼到這裏。

文章示範的 code 可以在 GitHub repo 找到。


  1. Intro
    簡單的開場白,介紹將會提及的內容。
  2. Architecture
    Architecture Components (MVVM)、Modularization 和這次示範 app 所用的 architecture。
  3. Endpoint
    示範 app 會用到的 API endpoint 和 API response 對應的 data class。
  4. Deserialization
    比較 Gson、Moshi、Kotlin Serialization,並為上篇準備的 data class 加上 Kotlin Serialization 的 annotation。
  5. Dependency injection
    為 project 加入 Dagger Hilt。
  6. HTTP Client
    Ktor client 簡介、基本用法及把 Ktor client 加進 Dagger dependency graph。當中提及 @Inject@InstallIn@Singleton@BindsOptionalOf 用法。
  7. Data layer implementation (1)
    車站及路綫 enum、domain layer repository interface、實作接駁 API endpoint、Dagger @Binds 用法及 Ktor client 跟 Retrofit 比較。
  8. Data layer implementation (2)
    API response data class 轉換到 domain layer data class 的 mapper。
  9. Date & time
    討論為甚麼要用 Java Time 而不是用 JDK 傳統的 CalendarDate 的原因、為 project 加入 desugering library 及介紹 Time Zone Database。
  10. Data layer testing (1)
    JUnit 4 test、MockK 及 Strikt 基本用法。
  11. Data layer testing (2)
    用 Strikt 為 object 每個 property 做 assertion。
  12. Data layer testing (3)
    Ktor client 搭配 logback-android 在 unit test 時出現錯誤的解決方法、運用 Ktor mock engine 模擬 server response。
  13. Data layer testing (4)
    運用上篇的 Ktor mock engine 測試觸發 HTTP request 的 code 和經過 Kotlin Serialization deserialize 後的 response object 是否合符預期,另加 Strikt 自定義的 assertion。
  14. Flipper
    安裝 Flipper(只在 debug build type 才安裝)、Dagger Hilt 的 @HiltAndroidApp@ApplicationContext 用法、Dagger @Qualifier@BindsOptionalOf 用法、OkHttp client 加入 interceptor。
  15. Domain layer implementation
    實作 use case。
  16. Domain layer testing
    Use case 的 unit test。
  17. Navigation (1)
    安裝 Navigation component,準備將會用到的 Fragment 和 layout XML 檔案、簡單介紹 Dagger Hilt 的 @AndroidEntryPoint 及在 Fragment 使用 data binding 時要注意的地方。
  18. Navigation (2)
    定義 Navigation graph 並設定轉頁的參數、設定 MainActivity 以顯示 navigation graph 的頁面及使用轉頁參數時要注意的地方。
  19. Station list screen (1)
    實作車站列表頁介面,使用了 RecyclerViewListAdapterDiffUtil、data binding,並示範了 Dagger Hilt 的 @ActivityScoped 用法。
  20. Station list screen (2)
    實作車站列表頁的 ViewModel,示範了 Dagger Hilt 的 @HiltViewModel、Kotlin Flow 的 ChannelStateFlow/MutableStateFlowcombinerepeatOnLifecycle 用法和 Navigation component 防止用戶快速連按轉頁按鈕導致 crash 的方法。
  21. ETA screen (1)
    介紹抵站時間頁的流程,實作頁面基本的 layout XML、RecyclerViewViewModelonBackPressedDispatcher,另外亦示範了 Kotlin Sequence
  22. Whistle proxy
    安裝 Whistle proxy、Whistle proxy 基本用法、在 Android 裝置安裝根證書、設定 network security configuration 及介紹 Proxy Toggle。
  23. ETA screen (2)
    SavedStateHandle 用法及以 Kotlin Flow 控制各式錯誤介面顯示。
  24. ETA screen (3)
    自動更新班次,介紹 Kotlin Coroutine 的 CoroutineScopeJobdelay 用法並使用 Java Time 的 Clock 取得當前時間。
  25. ETA screen (4)
    顯示錯誤 banner,介紹 Kotlin Coroutine 的 scan (runningFold) 用法,順帶講解 Kotlin Collection 的 foldreduce
  26. Station list screen testing
    安裝 Robolectric,為引用了 Android Context 的 class 寫 unit test;針對 Flipper 在 unit test 時報錯的解決方法;替換 Dispatchers.Main 及運用 Turbine 測試 Flow。
  27. ETA screen testing (1)
    示範 JUnit 4 parameterized test;介紹 cold flow (StateFlow) 跟 hot flow (SharedFlow) 的分別。
  28. ETA screen testing (2)
    運用 ThreeTen-Extra 提供的 MutableClock 及 Kotlin Coroutine 的 DelayController 改變時間並測試自動更新班次的邏輯。
  29. Leftover topics
    Two-way data binding、RecyclerView 局部更新、unidirectional data flow、instrumentation test 及 Coroutine dispatcher。
  30. Wrapping up
    參賽總結。
comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2021/03/android-sms-verification-apis/index.html b/2021/03/android-sms-verification-apis/index.html index 4b35d05b..b77d5e0f 100644 --- a/2021/03/android-sms-verification-apis/index.html +++ b/2021/03/android-sms-verification-apis/index.html @@ -1,5 +1,9 @@ Android SMS Verification APIs | EricLog -

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, +

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後再把內文的驗證碼輸入到 app 中。如果想省卻用戶輪入文字的話有一些 app 會透過 READ_SMS 權限讀取 SMS 內容來抽取驗證碼,但 Google Play 已經限制非預設短訊 app 不可以有 READ_SMS 權限

SMS Retriever API

如果有留意過一些 app 的驗證 SMS 的話,可以發現到有一些 SMS 內文結尾會加插一些英數字符。

SMS Retriever 短訊例子

當系統這種 SMS 後 app 就能自動填入那個 SMS 內的驗證碼,但特別的是那些 app 並沒有要求 READ_SMS 權限。其實它們是用了 Google 的 SMS Retriever APIs。簡單來講,就是 Google Play Services 代你的 app 拿了 READ_SMS 權限,由 Google Play Services 中央處理那些讀取 SMS 驗證碼的權限處理。最尾那一串英數字符就是給 Google Play Services 判斷這個 SMS 是要交到那個 app 處理。

其實 SMS Retriever API 用法其實不太複雜,大概步驟就是:

  1. Backend 發送 SMS 時在內文結尾加上一個特有的英數字符
  2. app 通知 SMS Retriever 開始監測系統接收到的 SMS
  3. SMS Retriever 接收到 SMS 後按照末端的英數字符通知對應的 app,app 透過 BroadcastReceiver 接收 SMS 內文,從中抽取驗證碼並繼續流程

SMS 內容

按照說明文檔,SMS 不能大過 140 bytes 和包含一個 11 位長的英數字符。那個英數字符是用 app 的 keystore 加上 application ID 生成出來,除了透過 shell script 生成之外,Google 還留了一個 AppSignatureHelper 方便大家生成那個字串。

先前的 SMS 例子開首有個 <#>,這其實是用來表示這個 SMS 是這是一個提供一次性密碼的 SMS。但現在 SMS Retriever 已經沒有這個規定。

開始監測

要觸發 SMS Retriever 開始監測收到的 SMS,只需要 call SmsRetrieverstartSmsRetriever 就可以了,監測期為五分鐘。1 下面的例子用了 kotlinx-coroutines-play-servicesawait 把原本 Google Play Services 的 Tasks API 變成 coroutine。2 緊記在通知 backend 發送時馬上通知 SMS Retriever,否則 SMS Retriever 就趕不來截取那個 SMS。

import com.google.android.gms.auth.api.phone.SmsRetriever
 import kotlinx.coroutines.tasks.await
@@ -41,8 +45,8 @@
     </application>
 </manifest>
 

上面介紹的 SMS Retriever API 只適用於能夠控制到 SMS 內文的情況。但如果那個 SMS 並不能自己控制內文的話(例如由銀行發出),那就要使用 SMS User Consent API。做法和 SMS Retriever API 相似,最大分別是系統會顯示一個 bottom sheet 詢問用戶是不是想把收到的那個 SMS 給予 app 讀取。如果同意的話 app 就會透過 BroadcastReceiver 接收 SMS 原文。

延伸閱讀


  1. SmsRetrieverApi#startSmsRetriever: Starts SmsRetriever, which waits for a matching SMS message until timeout (5 minutes). ↩︎

  2. kotlinx-coroutines-play-services 的 group 和 artifact 名稱是 org.jetbrains.kotlinx:kotlinx-coroutines-play-services。 ↩︎

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/03/notification-channel-custom-sound/index.html b/2021/03/notification-channel-custom-sound/index.html index 8cf052e4..60aeb68d 100644 --- a/2021/03/notification-channel-custom-sound/index.html +++ b/2021/03/notification-channel-custom-sound/index.html @@ -1,5 +1,5 @@ Notification Channel 自訂音效 | EricLog -

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各式 notification 的提示方法,例如有沒有音效、會不會彈出 heads-up notification 之類。如果 app 想自訂 notification 的聲音亦都要經 notification channel 設定(但用戶可以之後自行變更 notification channel 的音效)

但是要留意,設定自訂音效時那個 Uri 要用檔案名稱來設定,不要用 resource ID。以下是錯誤例子:

val uri = Uri.Builder()
+

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各式 notification 的提示方法,例如有沒有音效、會不會彈出 heads-up notification 之類。如果 app 想自訂 notification 的聲音亦都要經 notification channel 設定(但用戶可以之後自行變更 notification channel 的音效)

但是要留意,設定自訂音效時那個 Uri 要用檔案名稱來設定,不要用 resource ID。以下是錯誤例子:

val uri = Uri.Builder()
     .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
     .authority(context.packageName)
     .appendPath(R.raw.example_notification.toString())
@@ -29,8 +29,8 @@
     notificationManager?.createNotificationChannel(channel)
 }
 

還有一點要留意:刪除 notification channel 後再重新建立相同 channel ID 的 notification channel 並不能重設用戶對 channel 的設定(包括 app 在建立 notification channel 時所定義的通知音效)。這是一個防止濫用的機制。如果那個 app 不斷彈出通知的話那用戶可以把那個 app 的 notification channel 重要性降低(例如不彈出通知),但如果 app 刪除 channel 再建立 channel 就能把那些用戶設定重設的話那 notification channel 功能就變得多餘。故此 Android notification channel 的用戶設定是有記憶的,重新建立相同 channel ID 的 notification channel 只會把之前的設定帶回來。設定頁更會顯示那個 app 刪除 notification channel 的次數來提醒用戶可能這個 app 可能有 spam。如果之前已經錯誤地用了 resource ID 來設定 notification channel 音效的話解決方法就只有改用另一個全新的 notification channel。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/04/text-with-icon/index.html b/2021/04/text-with-icon/index.html index 11ae93fe..1c2ba6c7 100644 --- a/2021/04/text-with-icon/index.html +++ b/2021/04/text-with-icon/index.html @@ -1,5 +1,5 @@ 文字末端 icon 排版 | EricLog -

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而那個 Button 就固定在右邊。TextView 有 compound drawable,但如果想那個 icon 有 click listener 又用不了。所以最後都要分開 TextViewImageView/ImageButton 兩個 view。

失敗例子:ConstraintLayout horizontal chain

<androidx.constraintlayout.widget.ConstraintLayout
+

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而那個 Button 就固定在右邊。TextView 有 compound drawable,但如果想那個 icon 有 click listener 又用不了。所以最後都要分開 TextViewImageView/ImageButton 兩個 view。

失敗例子:ConstraintLayout horizontal chain

<androidx.constraintlayout.widget.ConstraintLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@android:color/holo_red_light">
@@ -157,8 +157,8 @@
         app:layout_constraintTop_toTopOf="parent" />
 </androidx.constraintlayout.widget.ConstraintLayout>
 
Flow 效果

實際效果

下圖示範了上面介紹的方法在實際運行時的效果:

由上至下:

  1. ConstraintLayout horizontal chain
  2. TableLayoutTableRow
  3. ConstraintLayout flow
  4. ConstraintLayout flow(Button 在左邊)

左邊是文字不太長的情況,右邊是文字過長的情況。

短文字和長文字的效果

完整的 XML layout file 已經放在 Gist

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-01-intro/index.html b/2021/09/2021-ithome-ironman-01-intro/index.html index 95be2619..b209b19d 100644 --- a/2021/09/2021-ithome-ironman-01-intro/index.html +++ b/2021/09/2021-ithome-ironman-01-intro/index.html @@ -1,7 +1,20 @@ 2021 iThome 鐵人賽 Day 1:Intro | EricLog -

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文

文章目錄

早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。

App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。

Wireframe

選擇用這個 app 做示範是因為我本身就想做這個功能(因為我本身有一個 app 是做這東西),另一個原因是顯示抵站時間頁不是單純的直接顯示伺服器傳回的內容,要有一些後期的處理和在用戶介面上需要分幾個狀態顯示內容。

在介紹如何做的同時我會順帶補充選用這個做法的原因和一些個人心得,藉此能提供一些官方使用說明沒有講的東西。

大概會包含的組件

  • Architecture Components
  • Data binding
  • RecyclerView
  • Navigation Components
  • Kotlin Coroutines
  • Kotlin Flow
  • Ktor Client
  • Kotlin serialization
  • Dagger Hilt

如果後段時間允許的話,或許會再加上 Compose 相關的內容。

對象

這個文章系列適合對 Kotlin 和 Android 開發有基本認識的人,因為文章不會每樣東西都解釋,亦不會每個步驟都附有截圖。簡單來說就是能寫到一個簡單的 Android app。(本身已經懂得 Activity、Fragment、RecyclerView 這些東西)

如果本身聽過上面那堆組件名稱但又不太知道實際怎樣用的話,這系列或許能幫到你。

GitHub Repository

示範 app 的源碼已經放在 GitHub

由於這篇文的讀者絕大部分都不是香港人,所以在這裏補充一下。「屯馬開通真的很興奮」之所以變成香港網路上的 meme 是因為調寄《綠䄂子》。《綠䄂子幻想曲 (Fantasia on Greensleeves)》是香港中文和英文科公開試聆聽考試會聽到的間場音樂,所以很多人每次聽到這首音樂都有不安情緒。

comments powered by Disqus
© 2024 EricLog +

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文

文章目錄

早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。

App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。

Wireframe

選擇用這個 app 做示範是因為我本身就想做這個功能(因為我本身有一個 app 是做這東西),另一個原因是顯示抵站時間頁不是單純的直接顯示伺服器傳回的內容,要有一些後期的處理和在用戶介面上需要分幾個狀態顯示內容。

在介紹如何做的同時我會順帶補充選用這個做法的原因和一些個人心得,藉此能提供一些官方使用說明沒有講的東西。

大概會包含的組件

  • Architecture Components
  • Data binding
  • RecyclerView
  • Navigation Components
  • Kotlin Coroutines
  • Kotlin Flow
  • Ktor Client
  • Kotlin serialization
  • Dagger Hilt

如果後段時間允許的話,或許會再加上 Compose 相關的內容。

對象

這個文章系列適合對 Kotlin 和 Android 開發有基本認識的人,因為文章不會每樣東西都解釋,亦不會每個步驟都附有截圖。簡單來說就是能寫到一個簡單的 Android app。(本身已經懂得 Activity、Fragment、RecyclerView 這些東西)

如果本身聽過上面那堆組件名稱但又不太知道實際怎樣用的話,這系列或許能幫到你。

GitHub Repository

示範 app 的源碼已經放在 GitHub

由於這篇文的讀者絕大部分都不是香港人,所以在這裏補充一下。「屯馬開通真的很興奮」之所以變成香港網路上的 meme 是因為調寄《綠䄂子》。《綠䄂子幻想曲 (Fantasia on Greensleeves)》是香港中文和英文科公開試聆聽考試會聽到的間場音樂,所以很多人每次聽到這首音樂都有不安情緒。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-02-architecture/index.html b/2021/09/2021-ithome-ironman-02-architecture/index.html index 783f8ece..b4255265 100644 --- a/2021/09/2021-ithome-ironman-02-architecture/index.html +++ b/2021/09/2021-ithome-ironman-02-architecture/index.html @@ -1,7 +1,19 @@ 2021 iThome 鐵人賽 Day 2:Architecture | EricLog -

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文

文章目錄

Architecture Components

以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture:

Android Developers 網站建議的 architecture

它主要特色是:

  • Activity/Fragment 負責 UI 部分
  • Repository 是外露資料處理的 method 讓 ViewModel call,使 ViewModel 無須得知資料的實質來源地
  • ViewModel 是把 Activity/Fragment 和 Repository 粘合在一起。它外露一些 method 觸發資料的處理(例如一個按鈕的 onClick listener 會 call ViewModel 的一個 method,ViewModel 會 call Repository 發送資料去 server),而 Repository 處理資料後的結果會透過 ViewModel 外露的 LiveData 然後在 Activity/Fragment observe 並按所收到的東西再更新 UI

如果 app 是一般 CRUD (Create, Read, Update, Delete) 類型的話這個架構應該足以應付。但如果 app 有很多 business logic 要處理的話(以計算地鐵車費為例,buiness logic 就是把 SQLite database 取得的由 A 站到 B 站的原車費按不同的情景加工處理,然後輸出折實價給 UI 顯示),這些 business logic code 可能會塞去 ViewModel 或 Repository,結果令那些 class 塞了太多 code。所以就出現了我們常常聽到的 Clean Architecture。在 Android app 一般的做法是把上圖的 Repository 和 ViewModel 加插 Use Case 或 Interactor 來塞 business logic,那些 use case 或 interactor 就是 Domain layer;Activity/Fragment 至 ViewModel 是 presentation layer;repository 至 SQLite/Retrofit 是 Data layer。

Modularization

Clean Architecture 沒有特定標準,所以有些比較講究的人會開設不同的 Gradle module 放置不同部分的 code 而不是把所有的 code 都放到同一個 module 內。至於如何界開不同的 module(即是「modularization」)亦都是一個不會有結論的話題。

最簡單的方法是:

  • Presentation(放 UI 相關的東西,即是 Activity/Fragment/ViewModel)
  • Domain(放 Use case 的 interface 和 concrete implementation、Repository interface)
  • Data(放 Repository 的 concrete implementation、供 Local 和 Remote implement 的 data source interface)
  • Local(處理本機資料來源,例如存取 Shared Preferences、Data Store、SQLite 之類並 implement data source interface 供 Data 使用)
  • Remote(處理遠端資料來源,例如 HTTP request 和 Web socket 之類並 implement data source interface 供 Data 使用)

好處是可以盡量把 module 設定成純 Kotlin/Java module,例如是 Domain、Data 和 Remote。那就減少亂寫 code 的情況(比如說是在 HTTP response error 時直接彈出 Toast 而不是在在 Presentation layer 決定用甚麼形式顯示 error)。還有是因為 Gradle module 之間的 dependency 能控制 module 內的 class 能否接觸到另一 module 的 class,所以比單純以 Java/Kotlin package 分隔不同 layer 更有保證。如果你打算做 Kotlin Multiplatform Mobile (KMM) 的話,刻意分割成純 Kotlin module 是必要的,因為 KMM 共用的 code 必須要是純 Kotlin 寫的,連調用 Java Standard Libaray 都不可以。

缺點是如果要修改某一個 feature 的話,你需要到每一個 module 修改 code,如果初接觸這種 modularization 方式的話很容易會被搞到頭昏腦脹,同時亦做不了 Play Feature Delivery。

另一種 modularization 方式是單純的按 feature 劃分,一個 feature 一個 module。一個 module 包含了該 feature 全部 layer 的 class,通常都是會用 Java/Kotlin package 劃分,這樣就可以做到 Play Feature Delivery。但缺點是要決定那些東西是跨 feature 共用,例如 SQLite database 是不是共用同一個,如果不共用如何保證 ACID?或者是兩個 feature 都要 call 同一個 API endpoint,那應該是共用同一套 code 還是把那些 code 抄到各自的 feature module 內?當出現了 common module 放這些跨 feature 的 code 後,common module 最終會不會變成另一個垃圾崗?另外,跨 module 的 navigation 亦是一大難題,即使出了 Navigation Component 仍未有完整方案,你不能在 feature module 的 project 使用 Navigation Component 的全部功能。這可能是你不能用到 safe args、IDE 會出現紅字、要自己寫額外 code 去封裝 Navigation Component 之類。

如果想同時做到按 feature 及 layer 分 module,可以每個 feature 都有對應的 layer module。即是Feature A 會有 Feature A 的 presentation、domain 和 data module。但這樣就會有太多 module。折中方案可以是 presentation 和 domain layer 就按 feature 劃分,data layer 就所有 feature 共用同一套 module。

我覺得如果是打算把現有 project 做 modularization 的話,按 layer 劃分或者是折中方案或許比較好。因為很多時候開始做的時候都是對全局沒有太深入的了解,未能為意那些地方是好多個 feature 都會同時用到。當你把舊有的 code 改成新寫法之後,對整個 app 有更深入的理解。那時候才決定最終用那款 modularization 還未算遲。因為新 code 都或多或少按 layer 和 feature 劃分,而且應該有 interface 分隔 layer 之間的 dependency,那時候再把 code 搬去另一 module 應該不用再大改現有的 code。

還有另一樣東西要留意是要不要每個 layer 都有對應的 data class 表達要處理的 data。相信大家都看過一些 project 是使用同一個 class 去接收 API response、表達 SQLite database table 的 row、加上 Parcelable 用來開啟 Activity/Fragment。如果 API response 的式樣跟你最後使用的需要是一致的話還好,但如果 API response 的式樣需要特別處理才能用的話這類多用途 data class 會很難理解。

示範 App

這個示範 app 都是用 MVVM,但就不會做 modularization,Layer 都是用 package 區分,layer 之間都是會有 interface 分隔。而因為列車抵站時間是每分鐘都在變,所以這個 app 不會做 local cache,只需要定時 call API 就可以了,那就把原本 data 和 remote layer 合併成 data layer。如果我們打算由一開到 Fragment 就 call backend API 並將 response 顯示在 UI 上,大概會是這樣:

示範 app 的 architecture

數字是步驟,橙色 Fragment 有個箭頭指向 Layout XML 和 ViewModel 意思是 Fragment 會建立 Binding 和 ViewModel instance。

comments powered by Disqus
© 2024 EricLog +

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文

文章目錄

Architecture Components

以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture:

Android Developers 網站建議的 architecture

它主要特色是:

  • Activity/Fragment 負責 UI 部分
  • Repository 是外露資料處理的 method 讓 ViewModel call,使 ViewModel 無須得知資料的實質來源地
  • ViewModel 是把 Activity/Fragment 和 Repository 粘合在一起。它外露一些 method 觸發資料的處理(例如一個按鈕的 onClick listener 會 call ViewModel 的一個 method,ViewModel 會 call Repository 發送資料去 server),而 Repository 處理資料後的結果會透過 ViewModel 外露的 LiveData 然後在 Activity/Fragment observe 並按所收到的東西再更新 UI

如果 app 是一般 CRUD (Create, Read, Update, Delete) 類型的話這個架構應該足以應付。但如果 app 有很多 business logic 要處理的話(以計算地鐵車費為例,buiness logic 就是把 SQLite database 取得的由 A 站到 B 站的原車費按不同的情景加工處理,然後輸出折實價給 UI 顯示),這些 business logic code 可能會塞去 ViewModel 或 Repository,結果令那些 class 塞了太多 code。所以就出現了我們常常聽到的 Clean Architecture。在 Android app 一般的做法是把上圖的 Repository 和 ViewModel 加插 Use Case 或 Interactor 來塞 business logic,那些 use case 或 interactor 就是 Domain layer;Activity/Fragment 至 ViewModel 是 presentation layer;repository 至 SQLite/Retrofit 是 Data layer。

Modularization

Clean Architecture 沒有特定標準,所以有些比較講究的人會開設不同的 Gradle module 放置不同部分的 code 而不是把所有的 code 都放到同一個 module 內。至於如何界開不同的 module(即是「modularization」)亦都是一個不會有結論的話題。

最簡單的方法是:

  • Presentation(放 UI 相關的東西,即是 Activity/Fragment/ViewModel)
  • Domain(放 Use case 的 interface 和 concrete implementation、Repository interface)
  • Data(放 Repository 的 concrete implementation、供 Local 和 Remote implement 的 data source interface)
  • Local(處理本機資料來源,例如存取 Shared Preferences、Data Store、SQLite 之類並 implement data source interface 供 Data 使用)
  • Remote(處理遠端資料來源,例如 HTTP request 和 Web socket 之類並 implement data source interface 供 Data 使用)

好處是可以盡量把 module 設定成純 Kotlin/Java module,例如是 Domain、Data 和 Remote。那就減少亂寫 code 的情況(比如說是在 HTTP response error 時直接彈出 Toast 而不是在在 Presentation layer 決定用甚麼形式顯示 error)。還有是因為 Gradle module 之間的 dependency 能控制 module 內的 class 能否接觸到另一 module 的 class,所以比單純以 Java/Kotlin package 分隔不同 layer 更有保證。如果你打算做 Kotlin Multiplatform Mobile (KMM) 的話,刻意分割成純 Kotlin module 是必要的,因為 KMM 共用的 code 必須要是純 Kotlin 寫的,連調用 Java Standard Libaray 都不可以。

缺點是如果要修改某一個 feature 的話,你需要到每一個 module 修改 code,如果初接觸這種 modularization 方式的話很容易會被搞到頭昏腦脹,同時亦做不了 Play Feature Delivery。

另一種 modularization 方式是單純的按 feature 劃分,一個 feature 一個 module。一個 module 包含了該 feature 全部 layer 的 class,通常都是會用 Java/Kotlin package 劃分,這樣就可以做到 Play Feature Delivery。但缺點是要決定那些東西是跨 feature 共用,例如 SQLite database 是不是共用同一個,如果不共用如何保證 ACID?或者是兩個 feature 都要 call 同一個 API endpoint,那應該是共用同一套 code 還是把那些 code 抄到各自的 feature module 內?當出現了 common module 放這些跨 feature 的 code 後,common module 最終會不會變成另一個垃圾崗?另外,跨 module 的 navigation 亦是一大難題,即使出了 Navigation Component 仍未有完整方案,你不能在 feature module 的 project 使用 Navigation Component 的全部功能。這可能是你不能用到 safe args、IDE 會出現紅字、要自己寫額外 code 去封裝 Navigation Component 之類。

如果想同時做到按 feature 及 layer 分 module,可以每個 feature 都有對應的 layer module。即是Feature A 會有 Feature A 的 presentation、domain 和 data module。但這樣就會有太多 module。折中方案可以是 presentation 和 domain layer 就按 feature 劃分,data layer 就所有 feature 共用同一套 module。

我覺得如果是打算把現有 project 做 modularization 的話,按 layer 劃分或者是折中方案或許比較好。因為很多時候開始做的時候都是對全局沒有太深入的了解,未能為意那些地方是好多個 feature 都會同時用到。當你把舊有的 code 改成新寫法之後,對整個 app 有更深入的理解。那時候才決定最終用那款 modularization 還未算遲。因為新 code 都或多或少按 layer 和 feature 劃分,而且應該有 interface 分隔 layer 之間的 dependency,那時候再把 code 搬去另一 module 應該不用再大改現有的 code。

還有另一樣東西要留意是要不要每個 layer 都有對應的 data class 表達要處理的 data。相信大家都看過一些 project 是使用同一個 class 去接收 API response、表達 SQLite database table 的 row、加上 Parcelable 用來開啟 Activity/Fragment。如果 API response 的式樣跟你最後使用的需要是一致的話還好,但如果 API response 的式樣需要特別處理才能用的話這類多用途 data class 會很難理解。

示範 App

這個示範 app 都是用 MVVM,但就不會做 modularization,Layer 都是用 package 區分,layer 之間都是會有 interface 分隔。而因為列車抵站時間是每分鐘都在變,所以這個 app 不會做 local cache,只需要定時 call API 就可以了,那就把原本 data 和 remote layer 合併成 data layer。如果我們打算由一開到 Fragment 就 call backend API 並將 response 顯示在 UI 上,大概會是這樣:

示範 app 的 architecture

數字是步驟,橙色 Fragment 有個箭頭指向 Layout XML 和 ViewModel 意思是 Fragment 會建立 Binding 和 ViewModel instance。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-03-endpoint/index.html b/2021/09/2021-ithome-ironman-03-endpoint/index.html index 5d474c30..07339ce4 100644 --- a/2021/09/2021-ithome-ironman-03-endpoint/index.html +++ b/2021/09/2021-ithome-ironman-03-endpoint/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 3:Endpoint | EricLog -

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文

文章目錄

我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。

打開「數據字典」PDF 檔,可以找到各車站及行車綫的代碼和 response body JSON object 的說明。當中我們主要用到的 property 大致上是:

  • plat 月台編號
  • time 時間(留意它的格式並非按照 ISO 8601
  • dest 目的地車站代碼
  • seq 次序(我們要按照這個順序顯示班次)

再看「港鐵實時列車服務資訊 API 規範文件」PDF 檔,它提供了幾個 HTTP response status code 的意義和一些 response body 例子。例如當事故發生時那個 API 會提供一段文字讓我們顯示。

Response data class

一般而言,我們都不會直接用 Android SDK 入面的 org.json 套件(即是 JSONObject 那些 class)來將 API response 的 JSON 轉成 Java/Kotlin object。因為要為每個 property 逐個寫 code,費時失事,所以我們都會用其他 deserialization library 去做 JSON 和 Java/Kotlin object 轉換。

在使用那些 library 之前,我們要準備 API response JSON 對應的 Kotlin data class。那個 data class 基本上就是要跟 JSON object 的結構相同,即是 response 的那個 property 是 string 就要在 data class 開對應的 string property。另外,我們只對整個 response 入面的某幾個 property 感興趣,所以在準備 data class 只針對那幾個 property 而造,其餘的東西我們就略過。

下面就是完成後的 data class,最尾的 companion object 放了幾個 constant 方便之後處理。

data class EtaResponse(
+

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文

文章目錄

我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。

打開「數據字典」PDF 檔,可以找到各車站及行車綫的代碼和 response body JSON object 的說明。當中我們主要用到的 property 大致上是:

  • plat 月台編號
  • time 時間(留意它的格式並非按照 ISO 8601
  • dest 目的地車站代碼
  • seq 次序(我們要按照這個順序顯示班次)

再看「港鐵實時列車服務資訊 API 規範文件」PDF 檔,它提供了幾個 HTTP response status code 的意義和一些 response body 例子。例如當事故發生時那個 API 會提供一段文字讓我們顯示。

Response data class

一般而言,我們都不會直接用 Android SDK 入面的 org.json 套件(即是 JSONObject 那些 class)來將 API response 的 JSON 轉成 Java/Kotlin object。因為要為每個 property 逐個寫 code,費時失事,所以我們都會用其他 deserialization library 去做 JSON 和 Java/Kotlin object 轉換。

在使用那些 library 之前,我們要準備 API response JSON 對應的 Kotlin data class。那個 data class 基本上就是要跟 JSON object 的結構相同,即是 response 的那個 property 是 string 就要在 data class 開對應的 string property。另外,我們只對整個 response 入面的某幾個 property 感興趣,所以在準備 data class 只針對那幾個 property 而造,其餘的東西我們就略過。

下面就是完成後的 data class,最尾的 companion object 放了幾個 constant 方便之後處理。

data class EtaResponse(
     /**
      * system status code.
      */
@@ -58,8 +68,8 @@
     }
 }
 

小結

我們已經準備好 response 的 data class。下一篇我們會介紹不同的 JSON deserialization library 並且將 EtaResponse 加上 deserialization library 的 annotation。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-04-deserialization/index.html b/2021/09/2021-ithome-ironman-04-deserialization/index.html index 4c7b9233..b1041bcb 100644 --- a/2021/09/2021-ithome-ironman-04-deserialization/index.html +++ b/2021/09/2021-ithome-ironman-04-deserialization/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 4:Deserialization | EricLog -

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文

文章目錄

JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,GsonMoshiKotlin serialization 都是熱門的選擇。

一般的 deserialization library 都是透過 Java/Kotlin class 的 property 名和 data type 來跟 JSON 做轉換,例如你在 Java class 有一個叫 createdAtString property,deserialization library 就會找 JSON object 有沒有一個叫 createdAt 的 property。如果有就把它以 String 的方式讀取,然後塞進去那個 Java class 的 createdAt property 入面。如果是 serialization 的話,就是把這個動作掉轉來做,就是看到 Java class 的 createdAt property 是 String,然後跟 Java String 最相近的 JSON data type 是 string,那 JSON object 就會出現 {"createdAt": "2021-09-01"}

Gson

Gson 是由 Google 開發,是上面三個 library 最早出現的一個,功能亦都很強大。例如可以設定 JSON 和 Java class property 之間的命名轉換規則(FieldNamingPolicy),亦都支援 streaming parsing,適合處理巨大的 JSON 文檔。

Moshi

Moshi 是 Square 開發的 JSON serialization/deserialization library。特色是它們功能上沒 Gson 那麼多,專注在主要的功能上,所以比 Gson 小巧。另外一大特色是它提供了 Kotlin codegen(一個 annotation processor 生成 Moshi 的 adapter),就是 Moshi 能感知你的 Kotlin class property 是不是 nullable 而決定是不是把那個 Kotlin class property 設定成那個 property 的預設值還是用 JSON document 找到的值。

Gson 是不會知道你那個 property 是不是 nullable,因為它是 Kotlin 的 language feature。所以在 Gson 做 deserialization 時的做法是:

  1. 用 Java reflection 執行 default constructor(即是沒有參數的 constructor)建立那一個 object
  2. 看到那 JSON 有一個 property,就用 Java reflection 找那個 class 有沒有對應的 property,如果有就把 JSON property 的值塞進去那個 Java object 入面

問題就是出現在第二步,即使你在 default constructor 入面把那些 property 塞了不是 null 的值(如果是 Kotlin data class 就是在定義 property 時提供了預設值),但是如果 JSON property 的 value 是 null 的話 Gson 仍會把 null 塞入去那個 property。如果那個是 Kotlin 的 non-null property 那就會去到調用 property 的地方突然出現 NullPointerException。同樣的情況如果是用 Moshi 配合 Kotlin 支援的話,它就會在 deserialization 拋出 JsonDataException,不會把問題延到其他地方才發現。

Kotlin Serialization

Kotlin Serialization 是那三個 library 最新的一個,我們將會用這個 library 來做 API JSON response 的 deserialization。這個 library 的特色是它是用 Kotlin compiler plugin 來生成 visitor 的 code(就是類似 Moshi 生成 adapter 般)、它本身的設計是支援多種格式諸如 Protobuf、Properties 等等還有是支援 Kotlin multiplatform。

Moshi 有個問題是如果 property 是 non-null 但 JSON 對應的 property 是 null 的話它就會 throw exception,但 Kotlin serialization 經過設定後可以讓它設定那個 property 的預設值而不是 throw exception。

如果你有 Protobuf 的需求的話,可以看看你是在那些地方用。如果是只有 app 自己在用(例如是 DataStore 的話)用 Kotlin serialization 都是不錯的選擇。但如果是和其他地方(例如跟 backend 之間通訊)那就不如考慮其他的 library。因為 Kotlin serialization 的野心是如果 backend、mobile 都是用 Kotlin 寫的話,那就只需要交換那個加了 Kotlin serialization annotation 的 data class 原檔就可以了,不需要再交換 proto 檔作中轉。另外,Kotlin serialization 是用 proto2,如果要用 proto3 還是用其他 library。

Response data class

我們選好了 Kotlin serialization,那就為先前的 data class 補回 annotation。其實要補回的 annotation 只有兩個:

  • @Serializable 放在 class 開頭
  • @SerialName 放在 property 開頭,用以指明 JSON 的 property 名
@Serializable
+

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文

文章目錄

JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,GsonMoshiKotlin serialization 都是熱門的選擇。

一般的 deserialization library 都是透過 Java/Kotlin class 的 property 名和 data type 來跟 JSON 做轉換,例如你在 Java class 有一個叫 createdAtString property,deserialization library 就會找 JSON object 有沒有一個叫 createdAt 的 property。如果有就把它以 String 的方式讀取,然後塞進去那個 Java class 的 createdAt property 入面。如果是 serialization 的話,就是把這個動作掉轉來做,就是看到 Java class 的 createdAt property 是 String,然後跟 Java String 最相近的 JSON data type 是 string,那 JSON object 就會出現 {"createdAt": "2021-09-01"}

Gson

Gson 是由 Google 開發,是上面三個 library 最早出現的一個,功能亦都很強大。例如可以設定 JSON 和 Java class property 之間的命名轉換規則(FieldNamingPolicy),亦都支援 streaming parsing,適合處理巨大的 JSON 文檔。

Moshi

Moshi 是 Square 開發的 JSON serialization/deserialization library。特色是它們功能上沒 Gson 那麼多,專注在主要的功能上,所以比 Gson 小巧。另外一大特色是它提供了 Kotlin codegen(一個 annotation processor 生成 Moshi 的 adapter),就是 Moshi 能感知你的 Kotlin class property 是不是 nullable 而決定是不是把那個 Kotlin class property 設定成那個 property 的預設值還是用 JSON document 找到的值。

Gson 是不會知道你那個 property 是不是 nullable,因為它是 Kotlin 的 language feature。所以在 Gson 做 deserialization 時的做法是:

  1. 用 Java reflection 執行 default constructor(即是沒有參數的 constructor)建立那一個 object
  2. 看到那 JSON 有一個 property,就用 Java reflection 找那個 class 有沒有對應的 property,如果有就把 JSON property 的值塞進去那個 Java object 入面

問題就是出現在第二步,即使你在 default constructor 入面把那些 property 塞了不是 null 的值(如果是 Kotlin data class 就是在定義 property 時提供了預設值),但是如果 JSON property 的 value 是 null 的話 Gson 仍會把 null 塞入去那個 property。如果那個是 Kotlin 的 non-null property 那就會去到調用 property 的地方突然出現 NullPointerException。同樣的情況如果是用 Moshi 配合 Kotlin 支援的話,它就會在 deserialization 拋出 JsonDataException,不會把問題延到其他地方才發現。

Kotlin Serialization

Kotlin Serialization 是那三個 library 最新的一個,我們將會用這個 library 來做 API JSON response 的 deserialization。這個 library 的特色是它是用 Kotlin compiler plugin 來生成 visitor 的 code(就是類似 Moshi 生成 adapter 般)、它本身的設計是支援多種格式諸如 Protobuf、Properties 等等還有是支援 Kotlin multiplatform。

Moshi 有個問題是如果 property 是 non-null 但 JSON 對應的 property 是 null 的話它就會 throw exception,但 Kotlin serialization 經過設定後可以讓它設定那個 property 的預設值而不是 throw exception。

如果你有 Protobuf 的需求的話,可以看看你是在那些地方用。如果是只有 app 自己在用(例如是 DataStore 的話)用 Kotlin serialization 都是不錯的選擇。但如果是和其他地方(例如跟 backend 之間通訊)那就不如考慮其他的 library。因為 Kotlin serialization 的野心是如果 backend、mobile 都是用 Kotlin 寫的話,那就只需要交換那個加了 Kotlin serialization annotation 的 data class 原檔就可以了,不需要再交換 proto 檔作中轉。另外,Kotlin serialization 是用 proto2,如果要用 proto3 還是用其他 library。

Response data class

我們選好了 Kotlin serialization,那就為先前的 data class 補回 annotation。其實要補回的 annotation 只有兩個:

  • @Serializable 放在 class 開頭
  • @SerialName 放在 property 開頭,用以指明 JSON 的 property 名
@Serializable
 data class EtaResponse(
     /**
      * system status code.
@@ -80,8 +90,8 @@
     kotlinx.serialization.KSerializer serializer(...);
 }
 
comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-05-dependency-injection/index.html b/2021/09/2021-ithome-ironman-05-dependency-injection/index.html index 295c8177..5bab6608 100644 --- a/2021/09/2021-ithome-ironman-05-dependency-injection/index.html +++ b/2021/09/2021-ithome-ironman-05-dependency-injection/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 5:Dependency injection | EricLog -

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文

文章目錄

談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。

Google 曾經推出過 Dagger Android 的 library,就是方便在 Android 開發時使用 Dagger。留意 Dagger 本身並不是只為 Android 設計,它本來是一個通用的 DI library。但推出後有些人發覺它甚至比原裝 Dagger 更難用。所以他們做了一個替代品:Dagger Hilt。其實 Dagger Hilt 就是把 Dagger 在 Android 的用法標準化,本來在 Dagger 一些由我們去定義的東西在 Dagger Hilt 變成由 Hilt 提供,我們要按照 Hilt 規定的方式做 DI。其中一個例子是 component 改為由 Hilt 按 Android 各部件的 lifecycle 預先安排好的 component。再加上 Hilt 的 Gradle plugin 和 Android Jetpack library 的配合,這樣就可以將平時用 Dagger 要寫的一大堆 boilerplate code 大大減少,而且在進入新的開發團隊時不用再浪費時間理解那些 AppComponent@Singleton 的實際意義。

現在討論 Dagger Hilt 沒有實際 code 做示範,感覺比較虛無,我們會在之後用到 Dagger Hilt 時再作講解。在結束本篇之前,相信大家心入面都會問一個問題:為甚麼要用 DI library。或許有不少人都是為用而用,或者是因為面試會問到所以要學習這些 library。我覺得最易理解為甚麼要用 DI 就是試試找一個沒有寫過單元測試 (unit test) 的項目把一些現有的 class 為它們寫單元測試,當你發現困難重重時(例如用了太多 singleton、很多地方寫死了在 unit test 很難換走導致很多情景無法製造出來)就會開始明白為甚麼要用 DI library。不過不用擔心,這次的示範 app 本身就有考慮到 unit testing 來寫的,我們會在下一篇開始用 Dagger Hilt。

準備工作

在最頂層的 build.gradle 和 app module 的 build.gradle 加上 Dagger Hilt 的 Gradle plugin 和 Dagger Hilt 相關的 dependency。

// root level build.gradle
+

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文

文章目錄

談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。

Google 曾經推出過 Dagger Android 的 library,就是方便在 Android 開發時使用 Dagger。留意 Dagger 本身並不是只為 Android 設計,它本來是一個通用的 DI library。但推出後有些人發覺它甚至比原裝 Dagger 更難用。所以他們做了一個替代品:Dagger Hilt。其實 Dagger Hilt 就是把 Dagger 在 Android 的用法標準化,本來在 Dagger 一些由我們去定義的東西在 Dagger Hilt 變成由 Hilt 提供,我們要按照 Hilt 規定的方式做 DI。其中一個例子是 component 改為由 Hilt 按 Android 各部件的 lifecycle 預先安排好的 component。再加上 Hilt 的 Gradle plugin 和 Android Jetpack library 的配合,這樣就可以將平時用 Dagger 要寫的一大堆 boilerplate code 大大減少,而且在進入新的開發團隊時不用再浪費時間理解那些 AppComponent@Singleton 的實際意義。

現在討論 Dagger Hilt 沒有實際 code 做示範,感覺比較虛無,我們會在之後用到 Dagger Hilt 時再作講解。在結束本篇之前,相信大家心入面都會問一個問題:為甚麼要用 DI library。或許有不少人都是為用而用,或者是因為面試會問到所以要學習這些 library。我覺得最易理解為甚麼要用 DI 就是試試找一個沒有寫過單元測試 (unit test) 的項目把一些現有的 class 為它們寫單元測試,當你發現困難重重時(例如用了太多 singleton、很多地方寫死了在 unit test 很難換走導致很多情景無法製造出來)就會開始明白為甚麼要用 DI library。不過不用擔心,這次的示範 app 本身就有考慮到 unit testing 來寫的,我們會在下一篇開始用 Dagger Hilt。

準備工作

在最頂層的 build.gradle 和 app module 的 build.gradle 加上 Dagger Hilt 的 Gradle plugin 和 Dagger Hilt 相關的 dependency。

// root level build.gradle
 buildscript {
     ext.daggerVersion = '2.38.1'
     repositories {
@@ -25,8 +35,8 @@
     }
 }
 

小結

  • Dagger Hilt 是建基於 Dagger 的 library,它就是為 Android 開發時用 Dagger 提供了一個固定的範式
  • Dagger Hilt 有 Gradle plugin 能減少平常用 Dagger 時所寫的 boilerplate code,加上其他 Jetpack library 的配套令我們用 Dagger 更自然(令人感覺上更似 dependency injection 而不是 service locator
  • 用 DI library 很大部分的原因是為了方便寫 unit test 時能把依賴的組件換成其他東西,這樣就能測試到不同的場景

補充資料

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-06-http-client/index.html b/2021/09/2021-ithome-ironman-06-http-client/index.html index f7cc9086..f209ff4b 100644 --- a/2021/09/2021-ithome-ironman-06-http-client/index.html +++ b/2021/09/2021-ithome-ironman-06-http-client/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 6:HTTP Client | EricLog -

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文

文章目錄

在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor

Ktor 是 JetBrains 出的 server library,就是用來開發 server side 的 web application。但它的功能比較簡單。不過我們不是用它的 server library,是用它的 client library。近年來 Kotlin 推廣用 Kotlin 寫跨平台應用(網頁、Android、iOS、backend),在 mobile app 那邊叫 Kotlin Multiplatform Mobile (KMM),它就是要用 Kotlin 來寫 Android 和 iOS 共用的部分(通常就是 business logic、接駁 backend 那部分),至於 UI 的部分就各自用回該平台的方法寫。正因為共通的部分必須要用純 Kotlin 來寫,code 不能引用 Java Standard Library 的東西,所以 OkHttp 和 Retrofit 就不能直接在 KMM 上面用,取而代之就是 Ktor Client。

因應不同平台實際處理 HTTP request 的 client(Ktor 稱為 engine)各有不同,Ktor 把這些 HTTP client 封裝了一次。例如在 Android 可以用 OkHttp、CIO,在 iOS 就是用 NSURLSession。所以在建立 Ktor client 時要因應不同平台有不同的設定,但你調用 Ktor 的地方就不用加那些 if (Android) { ... } 的東西。

以下是我們這次會用到的 dependency:

implementation "io.ktor:ktor-client-core:$ktorVersion"
+

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文

文章目錄

在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor

Ktor 是 JetBrains 出的 server library,就是用來開發 server side 的 web application。但它的功能比較簡單。不過我們不是用它的 server library,是用它的 client library。近年來 Kotlin 推廣用 Kotlin 寫跨平台應用(網頁、Android、iOS、backend),在 mobile app 那邊叫 Kotlin Multiplatform Mobile (KMM),它就是要用 Kotlin 來寫 Android 和 iOS 共用的部分(通常就是 business logic、接駁 backend 那部分),至於 UI 的部分就各自用回該平台的方法寫。正因為共通的部分必須要用純 Kotlin 來寫,code 不能引用 Java Standard Library 的東西,所以 OkHttp 和 Retrofit 就不能直接在 KMM 上面用,取而代之就是 Ktor Client。

因應不同平台實際處理 HTTP request 的 client(Ktor 稱為 engine)各有不同,Ktor 把這些 HTTP client 封裝了一次。例如在 Android 可以用 OkHttp、CIO,在 iOS 就是用 NSURLSession。所以在建立 Ktor client 時要因應不同平台有不同的設定,但你調用 Ktor 的地方就不用加那些 if (Android) { ... } 的東西。

以下是我們這次會用到的 dependency:

implementation "io.ktor:ktor-client-core:$ktorVersion"
 implementation "io.ktor:ktor-client-okhttp:$ktorVersion"
 implementation "io.ktor:ktor-client-logging:$ktorVersion"
 implementation "io.ktor:ktor-client-serialization:$ktorVersion"
@@ -74,8 +84,8 @@
     }
 }
 

Dagger module 是用來向 Dagger 提供一些 Dagger 未能自動 instantiate 的 object。如果你有看過 Dagger 的教學,都是要在 class 的 constructor 加上 @Inject 然後在執行時那些寫在 constructor 的 parameter 就會自然地取得那些 object。這個自動找到 dependency 塞入去 constructor 給你用的動作就是 Dagger 幫你做的,但它那個自動功能只能 inject 其他在 constructor 加了 @Inject 的 class。但遇到其他 third party 的 class 例如 Ktor client 又或者是 Android SDK 入面的 ConnectivityManager 之類就要靠我們自己寫 Dagger module 來提示 Dagger 如何 instantiate 這些 class。@Provides 就是用來手動教 Dagger 如何 instantiate 那個 object。@Provides 的 function 名是不重要,因為 Dagger 只看 parameter type 和 return type,但習慣上都是會跟 annotation 名作前綴。@Provides function parameter 就是用來取得其他 dependency,例如 provideKtorHttpClient 需要用到 HttpClientEngineHttpClientFeature<Logging.Config, Logging> 來 instantiate HttpClient

你或許會留意到第二個 parameter 被 Optional 包住,這個 Optional 是 Java 8 的東西,就是表示 HttpClientFeature<Logging.Config, Logging> 可能會有亦可能會無,有點像 nullable 的意思。因為那個被加註 @BindsOptionalOf 的 function,Dagger 能看懂 Optional。如果你全個 app 都沒有 @Provides 那個 logging feature 它亦不會 build fail。我把 logging 包了 Optional 是因為在 testing 或在 release build 時我們就不用為 HttpClient 加 logging。

在 module 除了看到 @Module 之外,還有 @InstallIn(SingletonComponent::class),這個 annotation 是 Hilt 的東西。Hilt 就是幫你訂好一個 Android app 會有那些 component。Component 主要作用是用來控制那些由 Dagger inject 的 dependency object 是不是在某範圍內重用還是每次要用到那個 dependency 都去 instantiate 一個新的。Hilt 的 SingletonComponent 就是跟 Application 共生死,它有對應的 scope 叫 @Singleton。上面 OkHttpClientHttpClient 都加了 @Singleton,意思是如果那個 SingletonComponent 都是同一個 instance 的話,那我經那個 component 拿到的 OkHttpClientHttpClient 都會是同一個 instance。正因為 Application 在執行時只會有一個 instance,所以 OkHttpClientHttpClient 就變相成為平時我們理解的 singleton 一樣,只是不是用 object class 而是靠 Dagger 控制。而 OkHttpClientHttpClient 要設成一個 app 共用同一個 instance 是因為 HTTP client 和 SQLite database connection 之類的東西建立成本比較高,所以不應每 call 一次 HTTP request 或 database query 都造一個全新的 connection。OkHttpClient 亦有同樣的提示:

OkHttp performs best when you create a single OkHttpClient instance and reuse it for all of your HTTP calls. This is because each client holds its own connection pool and thread pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request wastes resources on idle pools.

最後有一樣東西或許大家有留意到就是 DataModule 是 interface,入面只放 @BindsOptionalOf function(還有日後的 @Binds function),而內裏的 companion object 就放了 @Provides 的 function。這是因為按照 Dagger 網站的說明 @Provides 最好是 static 而 @Binds 就因為 Dagger 會生成對應的 code,所以用 interface 就夠了。

And for any module whose @Provides methods are all static, the implementation doesn’t need an instance at all.

@Binds methods are a drop-in replacement for Provides methods that simply return an injected parameter. Prefer @Binds because the generated implementation is likely to be more efficient.

Using @Binds is the preferred way to define an alias because Dagger only needs the module at compile time, and can avoid class loading the module at runtime.

我們已經準備好 Ktor client,下一篇我們會寫處理 backend API call 的部分。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-07-data-layer-implementation-1/index.html b/2021/09/2021-ithome-ironman-07-data-layer-implementation-1/index.html index 2032b0e0..bfc2e611 100644 --- a/2021/09/2021-ithome-ironman-07-data-layer-implementation-1/index.html +++ b/2021/09/2021-ithome-ironman-07-data-layer-implementation-1/index.html @@ -1,5 +1,20 @@ 2021 iThome 鐵人賽 Day 7:Data layer implementation (1) | EricLog -

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文

文章目錄

在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。

跨 layer 共用部分

不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。

enum class Line(val zh: String, val en: String) {
+

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文

文章目錄

在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。

跨 layer 共用部分

不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。

enum class Line(val zh: String, val en: String) {
     AEL("機場快綫", "Airport Express"),
     TCL("東涌綫", "Tung Chung Line"),
     TML("屯馬綫", "Tuen Ma Line"),
@@ -140,8 +155,8 @@
     }
 }
 

跟 Retrofit 的比較

如果是用 OkHttp 及 Retrofit 的話,寫法會比較簡潔一點。因為那些 parameter 和 API endpoint 的 URL 都是寫在 interface function 的 Retrofit 專門 annotation。現在我們用 Ktor client 感覺上會像直接用 OkHttp 發送 HTTP request 般,但有一點不同是 Ktor client 還是有跟 JSON deserialization library 有整合,只需一句 httpClient.get<EtaResponse> 就能拿到 deserialize 後的 object。如果你的 app 要駁不同的 API,每個的 base URL 都不相同的話或許用 Ktor client 這種寫法比起 Retrofit 要寫 interface 還要方便。

下一篇我們會實作 EtaResponseMapper

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-08-data-layer-implementation-2/index.html b/2021/09/2021-ithome-ironman-08-data-layer-implementation-2/index.html index fdde997e..b68df4bb 100644 --- a/2021/09/2021-ithome-ironman-08-data-layer-implementation-2/index.html +++ b/2021/09/2021-ithome-ironman-08-data-layer-implementation-2/index.html @@ -1,5 +1,24 @@ 2021 iThome 鐵人賽 Day 8:Data layer implementation (2) | EricLog -

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文

文章目錄

上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface:

interface Mapper<T, R> {
+

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文

文章目錄

上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface:

interface Mapper<T, R> {
     suspend fun map(o: T): R
 }
 

有些 Android Clean Architecture 會每個 layer 都準備一個對應的 mapper interface,這次示範我們就簡化這一部分,全部 layer 都共用同一個 mapper interface,不論是由高層次 layer 去低層次 layer 還是由低層次 layer 去高層次 layer 都一樣。因為這個 mapper 都是為了在寫 unit test 時可以 mock 那個 interface 而不是 mock 那個 concrete implementation,所以它是不是共用 interface 問題不大。

以下是整個 mapper class 的 code:

private val TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
@@ -58,8 +77,8 @@
 

不要忘記還有一樣東西要做的是要在 DataModule 加回 @Binds 的 function,否則在用的時候 Dagger 會報錯:

@Binds
 fun bindEtaResponseMapper(mapper: EtaResponseMapper): Mapper<HttpResponse, EtaResult>
 

到了這裏,data layer 的實作就完成了。完整的 code 可以直接去 GitHub repo 查閱。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-09-date-time/index.html b/2021/09/2021-ithome-ironman-09-date-time/index.html index 45eb4389..4053d546 100644 --- a/2021/09/2021-ithome-ironman-09-date-time/index.html +++ b/2021/09/2021-ithome-ironman-09-date-time/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 9:Date & time | EricLog -

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文

文章目錄

上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 InstantLocalDateTimeZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。

在 Java 8 之前,Java Standard Library 是有 CalendarDate 之類的 class,但它們本身的設計是古古怪怪。例如 Calendar 一月是用 0 表示,還有是沒有考慮到時區問題。所以後來就出現了 Joda Time 這個 library。受到 Joda Time 的啟發,之後就出了 JSR-310 的提案,最終就在 Java 8 的 Standard Library 加入了 Java Time。Java Time 除了之前用過的 DateTimeFormatterLocalDateTimeZonedDateTimeInstant 之外,還有其他常用的東西例如 ClockPeriodDuration 等等。它們都是用來表達不同的東西和方便我們計算關於日期時間的問題。例如我們會用 Instant 而不是一個 Long 的 variable 表示某個時刻、用 Duration 表示時段,這樣會令 code 更易理解,不用再擔心那個 Long 的單位是秒還是毫秒。另一個例子是計算 N 天後是幾年幾月幾日就不用再刻意把時分秒清零再加天數,因為可以用 LocalDate.now().plusDays(10) 就可以了。

由於舊版 Android 並未支援 Java 8 的功能,所以我們需要靠 desugaring 來為我們的 app 補上部分 Java 8 或以上的 language feature,當中包括大部分的 Java Time 功能。加入 desugaring library 的方法很簡單,就是在 app module 的 build.gradle 加上以下的東西:

android {
+

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文

文章目錄

上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 InstantLocalDateTimeZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。

在 Java 8 之前,Java Standard Library 是有 CalendarDate 之類的 class,但它們本身的設計是古古怪怪。例如 Calendar 一月是用 0 表示,還有是沒有考慮到時區問題。所以後來就出現了 Joda Time 這個 library。受到 Joda Time 的啟發,之後就出了 JSR-310 的提案,最終就在 Java 8 的 Standard Library 加入了 Java Time。Java Time 除了之前用過的 DateTimeFormatterLocalDateTimeZonedDateTimeInstant 之外,還有其他常用的東西例如 ClockPeriodDuration 等等。它們都是用來表達不同的東西和方便我們計算關於日期時間的問題。例如我們會用 Instant 而不是一個 Long 的 variable 表示某個時刻、用 Duration 表示時段,這樣會令 code 更易理解,不用再擔心那個 Long 的單位是秒還是毫秒。另一個例子是計算 N 天後是幾年幾月幾日就不用再刻意把時分秒清零再加天數,因為可以用 LocalDate.now().plusDays(10) 就可以了。

由於舊版 Android 並未支援 Java 8 的功能,所以我們需要靠 desugaring 來為我們的 app 補上部分 Java 8 或以上的 language feature,當中包括大部分的 Java Time 功能。加入 desugaring library 的方法很簡單,就是在 app module 的 build.gradle 加上以下的東西:

android {
     // ...
     compileOptions {
         coreLibraryDesugaringEnabled true
@@ -16,8 +26,8 @@
     // ...
 }
 

Desugaring 的原理其實就是把那些 Java 8 或以上能夠 backport 到的 language feature 在 compile 時幫你補上去 APK/AAB 內,所以不論 Android 版本是新還是舊都能行到那段 code,反正都是用你 APK/AAB 提供的 class 執行。

加入了 desugaring 的 DEX 檔案包含了 Java Time 的 class

在未有 desugering 之前,一般都會用 ThreeTen Backport 代替 Java Time。

時區問題

另一樣想借本篇討論的是時區的問題。在台灣或者香港都是 UTC+08:00,而且沒有日光節約時間 (daylight saving time, DST)。所以很多時在設計系統都沒有認真考慮時區問題(或者沒有意識到有這個問題),全部時間儲存都一概使用 UNIX timestamp。但如果系統日後需要支援多時區時就很難改得動了。

首先我們要了解 Time Zone Database 和 time zone ID。Time Zone Database 是由 IANA 管理的時區資料庫(IANA 就是管理域名的那個機構),那個 database 就是要記錄世界各地以前至未來已知的時區資訊,當中包括日光節約時間的切換規則。

那 time zone ID 就是我們平常寫 code 看到的 Asia/Hong_KongAsia/TaipeiAsia/ShanghaiAmerica/New_York 之類的東西。這些 ID 就是找一些有代表性的地名來命名,代表性的意思是指以時區、政府、以往實行過的時區之類有獨特性。雖然 Asia/Hong_KongAsia/TaipeiAsia/Shanghai 在現在都是指向 UTC+08:00,但為了能順利地轉換以前的日期時間仍要保留三個不同的 ID 表示。如果你有下載過 Time Zone Database 的話,用普通的純文字編輯器打開就會看到它會紀錄每個 time zone ID 何時選用那個 UTC 偏移量 (offset) 和有沒有執行夏令時間的資訊,還有更多的是有間該 time zone ID 的相關文獻。如果想了解更多那個地方時區的歷史的話 Time Zone Database 是不容錯過。

Time Zone Database 香港部分

Time Zone Database 香港的部分

其中一個有趣的東西是 Time Zone Database 跟中國大陸相關的有好幾個 ID,這是因為中國大陸曾經分被劃分為五個時區

  • Asia/Shanghai 上海 (UTC+08:00)
  • Asia/Urumqi 烏魯木齊 (UTC+06:00)
  • Asia/Harbin 哈爾濱,現在指向 Asia/Shanghai
  • Asia/Chongqing 重慶,現在指向 Asia/Shanghai
  • Asia/Kashgar 喀什,現在指向 Asia/Urumqi

這些重新指向的 ID 都是為了向後兼容,即是如果表達一個以前的當地時間我們仍可以配搭這些 ID 從而計算出 UNIX timestamp 或者反向計算出當地時間。

Windows 10 UTC+08:00 時區

Windows 時區列表出現好幾個地名反映了以前那些地方都採用不同時區

除了一般的時區問題之外,部分地方會實行日光節約時間 (Daylight Saving Time, DST)。意思是一年會切換兩次時區:大約在春季左右會找一天把時間調快一小時;在秋季又會再把時間調慢一小時還原。用意是因為夏天的日照時間長,把日常活動都調快一小時就能接觸更多陽光,從而節省能源(例如開少一小時電燈)。當然去到今時今日還能不能節省能源已成疑問,加上切換時間那兩天對工作和生活都造成影響。所以歐盟有考慮過廢除日光節約時間,但目前尚未實行。

其實寫了那麼多 Time Zone Database 的東西都是想指出 UNIX timestamp 不能萬能,尤其是用於表達將來的時間。因為時區可以隨時因為各地政府的政策變更(例如會否在將來取消日光節約時間),所以如果要儲存幾年後的某月某日早上 9 時要開會的話,我們應該儲存當地時間及 time zone ID,不是 UNIX timestamp 或者當地時間及 UTC 偏移量。這樣即使政府改變時區都不會影響到儲存的資料(因為可以靠更新 Time Zone Database 以取得正確結果)。其中一個改變時區的例子是北韓,在 2015 年 8 月 15 日至 2018 年 5 月 4 日期間改用 UTC+08:30,其後改回跟南韓一樣 UTC+09:00。但如果是儲存過去的日期時間的話用 UNIX timestamp 就沒有大問題,因為過去的東西不會再改。

Time Zone Database 更新

IANA 會跟據各地政府對時區的變更更新 Time Zone Database,所以一年可能會發布好幾個版本。這個 database 在不少地方用到,例如大家平常使用的作業系統、JRE 等等。它們都會透過系統更新或 JRE 版本更新來更新它們內裏所用的 Time Zone Database。有關 Android 系統更新 database 的方法可以參考 AOSP 網站。

如果很在乎 Time Zone Database 是不是最新版的話,目前似乎只有 TickTock(不是抖音)和用回 ThreeTen Backport

另外,在使用 Java Time 的 method 前,緊記檢查 desugaring 支援的 class 和對 Java Time 的特別說明,因為 backport 始終有部分的實作仍依賴原系統的實作。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-10-data-layer-testing-1/index.html b/2021/09/2021-ithome-ironman-10-data-layer-testing-1/index.html index 7d8d1069..b8c412eb 100644 --- a/2021/09/2021-ithome-ironman-10-data-layer-testing-1/index.html +++ b/2021/09/2021-ithome-ironman-10-data-layer-testing-1/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 10:Data layer testing (1) | EricLog -

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文

文章目錄

在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(StriktMockK):

dependencies {
+

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文

文章目錄

在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(StriktMockK):

dependencies {
     testImplementation platform("io.strikt:strikt-bom:$striktVersion")
     testImplementation "io.strikt:strikt-core"
     testImplementation "io.strikt:strikt-mockk"
@@ -64,8 +74,8 @@
     expectThat(mapper.map(response)).isA<EtaResult.InternalServerError>()
 }
 

最後一句 expectThat 是 Strikt 的寫法,expectThat 入面放的是要檢驗的項目,然後就可以繼續串接 Strikt 的 method 就能針對它做檢驗。

因為篇幅有點長,我們在下一篇示範正常輸出班次的情景。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-11-data-layer-testing-2/index.html b/2021/09/2021-ithome-ironman-11-data-layer-testing-2/index.html index 47321c5e..0a6598d4 100644 --- a/2021/09/2021-ithome-ironman-11-data-layer-testing-2/index.html +++ b/2021/09/2021-ithome-ironman-11-data-layer-testing-2/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 11:Data layer testing (2) | EricLog -

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文

文章目錄

今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response:

val response = mockHttpResponse(
+

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文

文章目錄

今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response:

val response = mockHttpResponse(
     statusCode = HttpStatusCode.OK,
     etaResponse = EtaResponse(
         status = EtaResponse.STATUS_NORMAL,
@@ -112,8 +122,8 @@
   ✗ is equal to Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk/alert/alert_title_wap.html)
           found Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk)
 

EtaResponseMapperTest 的其他 test case 其實寫法都大同小異,所以我就不逐一介紹。有興趣的話可以直接去 GitHub 看 code

下一篇會寫 EtaRepositoryImplTest

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-12-data-layer-testing-3/index.html b/2021/09/2021-ithome-ironman-12-data-layer-testing-3/index.html index 3236e53c..17f461a2 100644 --- a/2021/09/2021-ithome-ironman-12-data-layer-testing-3/index.html +++ b/2021/09/2021-ithome-ironman-12-data-layer-testing-3/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 12:Data layer testing (3) | EricLog -

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文

文章目錄

上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。

Logback 的特別設定

我們先前在設定 Ktor client 時幫它加了 logging 功能,這樣我們就可以在 Logcat 看到 request 和 response 的資訊,方便 debug。而它所用的 logger 是按照 Simple Logging Facade for Java (SLF4J) 規格。SLF4J 其實是一個 Java 有名的 logging interface,如果一些組件或 library 想用 logging 功能的話,它們可以用 SLF4J 的 interface 發送 log 到 logger,但最終所用的 logger 是由用那些組件的一方控制。這樣就不會把 log 亂射和可以把 log 集中處理。在 Android 的話,我們會用 logback-android

把這段內容放到本篇才說是因為我們會用到 Ktor 的 mock client。如果我們只加了 logback-android 的話,在執行 unit test 時就會出現以下錯誤(節錄):

SLF4J: Class path contains multiple SLF4J bindings.
+

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文

文章目錄

上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。

Logback 的特別設定

我們先前在設定 Ktor client 時幫它加了 logging 功能,這樣我們就可以在 Logcat 看到 request 和 response 的資訊,方便 debug。而它所用的 logger 是按照 Simple Logging Facade for Java (SLF4J) 規格。SLF4J 其實是一個 Java 有名的 logging interface,如果一些組件或 library 想用 logging 功能的話,它們可以用 SLF4J 的 interface 發送 log 到 logger,但最終所用的 logger 是由用那些組件的一方控制。這樣就不會把 log 亂射和可以把 log 集中處理。在 Android 的話,我們會用 logback-android

把這段內容放到本篇才說是因為我們會用到 Ktor 的 mock client。如果我們只加了 logback-android 的話,在執行 unit test 時就會出現以下錯誤(節錄):

SLF4J: Class path contains multiple SLF4J bindings.
 SLF4J: Found binding in [jar:file:/C:/Users/Eric/.gradle/caches/transforms-3/ff97545d615cededbad0c653ea1a09c7/transformed/jetified-logback-android-2.0.0-runtime.jar!/org/slf4j/impl/StaticLoggerBinder.class]
 SLF4J: Found binding in [jar:file:/C:/Users/Eric/.gradle/caches/transforms-3/afd61d02052ffb4feaec186ea7b45062/transformed/jetified-logback-android-2.0.0/jars/classes.jar!/org/slf4j/impl/StaticLoggerBinder.class]
 SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
@@ -113,8 +123,8 @@
     at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
     at net.swiftzer.etademo.data.EtaRepositoryImplTest.getEta normal(EtaRepositoryImplTest.kt:45)
 

出現「This job has not completed yet」的原因應該是 Ktor 開了新 thread 做 HTTP request/respond。但我找不到地方讓我換走 Executor 或者 Dispatcher(但 HttpClientEngineConfig 可以改變 threadsCount)。而 Ktor 網站的示範亦都是用 runBlocking,可能它真的沒有辦法用 runBlockingTest

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-13-data-layer-testing-4/index.html b/2021/09/2021-ithome-ironman-13-data-layer-testing-4/index.html index 985e42c6..6eeea251 100644 --- a/2021/09/2021-ithome-ironman-13-data-layer-testing-4/index.html +++ b/2021/09/2021-ithome-ironman-13-data-layer-testing-4/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 13:Data layer testing (4) | EricLog -

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文

文章目錄

上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。

Test case 的目標是:

  1. 檢查交去 Ktor client 的 request parameter(即是語言、路綫、車站)是否正確
  2. 看看加在 Ktor client 的 Kotlinx serialization 能否正常地把我們提供的 response JSON 轉成 EtaResponse

由於我們已針對 EtaResponseMapper 寫了 unit test,我們就乾脆 mock 那個 mapper 然後隨便 return 一個 EtaResult.Success 就算了。當然你亦可以 instantiate 一個真的 mapper 來做轉換,因為本身有 mapper 的 unit test,所以即使 repository test 有錯都可以容易剔除 mapper 有錯這個因素。

同樣地,我們都是用之前寫的 mockHttpClient 來假裝 server response。首先我們會檢查 HTTP request 的 query parameter 是否正確。

@Test
+

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文

文章目錄

上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。

Test case 的目標是:

  1. 檢查交去 Ktor client 的 request parameter(即是語言、路綫、車站)是否正確
  2. 看看加在 Ktor client 的 Kotlinx serialization 能否正常地把我們提供的 response JSON 轉成 EtaResponse

由於我們已針對 EtaResponseMapper 寫了 unit test,我們就乾脆 mock 那個 mapper 然後隨便 return 一個 EtaResult.Success 就算了。當然你亦可以 instantiate 一個真的 mapper 來做轉換,因為本身有 mapper 的 unit test,所以即使 repository test 有錯都可以容易剔除 mapper 有錯這個因素。

同樣地,我們都是用之前寫的 mockHttpClient 來假裝 server response。首先我們會檢查 HTTP request 的 query parameter 是否正確。

@Test
 fun `getEta normal`() {
     runBlocking {
         val (client, requestSlot) = mockHttpClient(
@@ -91,8 +101,8 @@
     }
 )
 

因為寫法都是大同小異,所以就不再逐一介紹。其餘的 test caseJSON response 檔可以到 GitHub repo 查閱,而 data layer 的 unit test 部分亦告一段落。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-14-flipper/index.html b/2021/09/2021-ithome-ironman-14-flipper/index.html index 4c7ba50b..cc3e0b88 100644 --- a/2021/09/2021-ithome-ironman-14-flipper/index.html +++ b/2021/09/2021-ithome-ironman-14-flipper/index.html @@ -1,5 +1,18 @@ 2021 iThome 鐵人賽 Day 14:Flipper | EricLog -

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文

文章目錄

在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper

Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。

Flipper 的前身是 Stetho,或許大家以前有見過,就是 Facebook 借 Chrome DevTools 介面來提供 HTTP traffic、UI layout、Shared Preferences、SQLite database 查閱功能的那個 library。但因為用 Chrome DevTools 的界面做 UI,功能就會受到 DevTools 的限制,不能提供超越 DevTools 界面的功能。還有是 iOS 又不能用,app 結束後那個 DevTools 視窗就要作廢不能重用。所以就促成 Facebook 開發 Flipper(前稱 Sonar)。Flipper 是一個用 Electron 做的 desktop app 來做介面,並提供 Android 和 iOS SDK 把 app 的內容交予 Flipper desktop app。為了方便我們做 UI 時能看清楚 HTTP request,我們現在要做的是把 SDK 加到 app 入面。

首先是 app module 的 build.gradle 加上以下的 dependency:

debugImplementation "com.facebook.flipper:flipper:$flipperVersion"
+

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文

文章目錄

在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper

Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。

Flipper 的前身是 Stetho,或許大家以前有見過,就是 Facebook 借 Chrome DevTools 介面來提供 HTTP traffic、UI layout、Shared Preferences、SQLite database 查閱功能的那個 library。但因為用 Chrome DevTools 的界面做 UI,功能就會受到 DevTools 的限制,不能提供超越 DevTools 界面的功能。還有是 iOS 又不能用,app 結束後那個 DevTools 視窗就要作廢不能重用。所以就促成 Facebook 開發 Flipper(前稱 Sonar)。Flipper 是一個用 Electron 做的 desktop app 來做介面,並提供 Android 和 iOS SDK 把 app 的內容交予 Flipper desktop app。為了方便我們做 UI 時能看清楚 HTTP request,我們現在要做的是把 SDK 加到 app 入面。

首先是 app module 的 build.gradle 加上以下的 dependency:

debugImplementation "com.facebook.flipper:flipper:$flipperVersion"
 debugImplementation "com.facebook.soloader:soloader:$soloaderVersion"
 debugImplementation "com.facebook.flipper:flipper-network-plugin:$flipperVersion"
 

留意 Flipper Android SDK 在 Flipper 網站和 GitHub 的最新版本未必能在 Maven Central 找到,所以最好還是先檢查 Maven Central 那邊最新版本是甚麼

由於我們不想在 app 日後上架時都夾附 Flipper,我們就借用 build type 來控制:debug 才能用 Flipper;release 就不要有 Flipper 的 dependency。所以我們這次用 debugImplementation 而不是 implementation

接下來就是按照 Flipper 網站的指示Application class 的 onCreate 加上 Flipper 初始化的 code。不過我們應該還未有自己的 Application class,現在就先建立一個叫 EtaDemoAppApplication subclass。

@HiltAndroidApp
@@ -114,8 +127,8 @@
     }
 }
 

由於這個 FlipperHelper 沒有在 constructor 用到那些 Flipper plugin,所以我們就不用寫一個 FlipperReleaseModule 之類的東西,就是這麼簡單。


你或許會問為甚麼我們不乾脆把 EtaDemoAppdebug release 兩個版本而要另外開一個 FlipperHelper 分兩邊放。這是因為 application class 通常都會有其他東西,為了一個 Flipper 而要維護兩個(或更多個)application class 是費時失事。如果 build variant 再增加下去的話,我會建議另開兩個 Gradle module 放有 Flipper 和無 Flipper 版的 FlipperHelper ,然後再 xxxImplementation project(':flipper')project(':flipper-noop') 這樣。其實 Flipper 本身都有提供 no-op 版 artifact,但不是所有 Flipper plugin 都有提供 no-op 版 artifact,所以還在在本篇示範了如何自行做 no-op。

小結

我們現在已經加了 Flipper,它的 network 功能我們之後會用到(我們要看它有沒有定時自動更新班次)。為甚麼要用一篇文章寫 Flipper 呢?這是因為以前工作時經常被 backend 同事問到 Android app 如何 call 那個 endpoint,最直觀的方法就是用 proxy 或者 Flipper 這類工具先看看 network traffic 然後才在 app 的 source code 找 call 那個 endpoint 的位置。這樣我就不用把 code 由頭看到尾,還有是 backend 同事能自己直接試,不用再走來問我這頁 call 了甚麼 endpoint、payload 是甚麼之類的問題。

當然,如果要做到改 response、延遲 request/response 這些功能的話,還是需要用到 proxy server。我平常用開的 proxy server 是 Whistle,proxy server 還有其他選擇,例如 CharlesProxymanFiddler 等等。

另外亦借安裝 Flipper 介紹了 Dagger Hilt 比 Dagger 簡化了甚麼地方,Dagger 的 scope 和 qualifier 用法,還有是 build type 的用法。

這次示範的完整 code 可以在 GitHub repo 找到。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/09/2021-ithome-ironman-15-domain-layer-implementation/index.html b/2021/09/2021-ithome-ironman-15-domain-layer-implementation/index.html index 61b073f4..e9be185a 100644 --- a/2021/09/2021-ithome-ironman-15-domain-layer-implementation/index.html +++ b/2021/09/2021-ithome-ironman-15-domain-layer-implementation/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 15:Domain layer implementation | EricLog -

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文

文章目錄

經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 ActivityFragmentViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。

其實叫 interactor 或者 use case 我覺得沒有太大分別,反正它們都是把背後的東西(例如與 backend API、本地 database 互動、business logic)隱藏起來,並且將「收藏」功能這樣改一處地方就能通知另一處要更新的 logic 移離 presentation layer(另一個例子是收到 push notification 後要更新資料)。通常一個 use case 只會做一個動作,如果是要做齊增刪查改 (create, read, update, delete) 的話那就會有四個 use case。有些人會偏好做一個通用的 use case interface(就像我們做 mapper 要有個 mapper interface 般要所有 mapper 都 implement 同一個 interface),但缺點是如果要傳遞參數的話就會變得很麻煩,每個 use case 都要做一個 data class 去載住參數(或者是用 Map 裝着)。所以我們這次是每一個 use case 都做一個專門的 interface。

車站列表

示範 app 會有兩個 use case,第一個是提供車站列表供用戶選取查閱班次的車站。首先準備它的 interface class GetLinesAndStationsUseCase

interface GetLinesAndStationsUseCase {
+

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文

文章目錄

經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 ActivityFragmentViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。

其實叫 interactor 或者 use case 我覺得沒有太大分別,反正它們都是把背後的東西(例如與 backend API、本地 database 互動、business logic)隱藏起來,並且將「收藏」功能這樣改一處地方就能通知另一處要更新的 logic 移離 presentation layer(另一個例子是收到 push notification 後要更新資料)。通常一個 use case 只會做一個動作,如果是要做齊增刪查改 (create, read, update, delete) 的話那就會有四個 use case。有些人會偏好做一個通用的 use case interface(就像我們做 mapper 要有個 mapper interface 般要所有 mapper 都 implement 同一個 interface),但缺點是如果要傳遞參數的話就會變得很麻煩,每個 use case 都要做一個 data class 去載住參數(或者是用 Map 裝着)。所以我們這次是每一個 use case 都做一個專門的 interface。

車站列表

示範 app 會有兩個 use case,第一個是提供車站列表供用戶選取查閱班次的車站。首先準備它的 interface class GetLinesAndStationsUseCase

interface GetLinesAndStationsUseCase {
     operator fun invoke(): Map<Line, Set<Station>>
 }
 

用了 operator fun invoke() 是因為之後 ViewModel 可以把 variable 當 method call,這樣看起上來更簡潔。這個 syntax 在 Kotlin 叫 operator overloading。以下就是例子:

val getLinesAndStations: GetLinesAndStationsUseCase
@@ -50,8 +60,8 @@
     fun bindGetLinesAndStationsUseCase(impl: GetLinesAndStationsUseCaseImpl): GetLinesAndStationsUseCase
 }
 

完整的 code 可以到 GitHub repo 查閱,下一篇我們會幫這兩個 use case implementation 寫 unit test。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-16-domain-layer-testing/index.html b/2021/10/2021-ithome-ironman-16-domain-layer-testing/index.html index 4ea6021f..38d0c053 100644 --- a/2021/10/2021-ithome-ironman-16-domain-layer-testing/index.html +++ b/2021/10/2021-ithome-ironman-16-domain-layer-testing/index.html @@ -1,5 +1,20 @@ 2021 iThome 鐵人賽 Day 16:Domain layer testing | EricLog -

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文

文章目錄

今天會為上一篇所寫的兩個 use case 加上 unit test。

GetLinesAndStationsUseCaseImplTest

這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepositorygetLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。

private val DUMMY_DATA = mapOf(
+

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文

文章目錄

今天會為上一篇所寫的兩個 use case 加上 unit test。

GetLinesAndStationsUseCaseImplTest

這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepositorygetLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。

private val DUMMY_DATA = mapOf(
     Line.AEL to setOf(Station.HOK, Station.KOW),
     Line.TKL to setOf(Station.NOP, Station.YAT, Station.LHP),
 )
@@ -153,8 +168,8 @@
     expectThat(result).isEqualTo(incident)
 }
 

基本上都是比對 return value 是不是跟 EtaRepository return 出來的一樣,其餘的 test case 都是同樣寫法,所以我就不逐一貼出來。完整的 test class 可以到 GitHub repo 查看。

下一篇我們會開始做 presentation layer。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-17-navigation-1/index.html b/2021/10/2021-ithome-ironman-17-navigation-1/index.html index e292da60..2e33f144 100644 --- a/2021/10/2021-ithome-ironman-17-navigation-1/index.html +++ b/2021/10/2021-ithome-ironman-17-navigation-1/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 17:Navigation (1) | EricLog -

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文

文章目錄

經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 ActivityFragmentViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。

我們這個示範 app 會採用 single activity app 的做法,即是整個 app 只會有一個 Activity,所有顯示的頁面都是用 Fragment 來裝住。如果要由一頁轉去另一頁的話原理就是用 FragmentManager 切換顯示另一個 Fragment。不過我們不會直接接觸 FragmentManager,而是用 AndroidX 的 Navigation component 來幫我們處理。為甚麼原本能用多個 Activity 就做到的東西要轉用 single activity app 來做呢?主要原因是處理 deep link 的話 app 只有單一 Activity 是遠較多個 Activity 的 app 容易控制,單是 Manifest 入面 <activity>android:launchMode 就搞到頭疼,後來更變成 Android 面試的經典題目。如果完全轉用 single activity 的做法的話基本上除了要做「一 app 多開」的效果外,基本上都不用特別處理 android:launchMode。「一 app 多開」的正式名稱是 task。意思是一個 app 可以在系統的「recent apps」顯示好幾個視窗,例如以前的 Chrome 開新 tab 都是用這個功能來做到。這個功能在 Word 這類應用非常合適,配合 split screen 來用就可以同時上下顯示兩個 Word 文件並同時編輯。

說回 navigation 的部分,AndroidX 的 Navigation component 除了處理換頁時的 Fragment 切換和 deep link 之外,還有是管理每頁傳入的參數、換頁動畫,配搭 Dagger Hilt 的話更可以設定某些 object 的 scope 是跟 navigation graph 共生死。

安裝 Navigation component

首先在 project 的 build.gradle 加入 safe args Gradle plugin:

dependencies {
+

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文

文章目錄

經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 ActivityFragmentViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。

我們這個示範 app 會採用 single activity app 的做法,即是整個 app 只會有一個 Activity,所有顯示的頁面都是用 Fragment 來裝住。如果要由一頁轉去另一頁的話原理就是用 FragmentManager 切換顯示另一個 Fragment。不過我們不會直接接觸 FragmentManager,而是用 AndroidX 的 Navigation component 來幫我們處理。為甚麼原本能用多個 Activity 就做到的東西要轉用 single activity app 來做呢?主要原因是處理 deep link 的話 app 只有單一 Activity 是遠較多個 Activity 的 app 容易控制,單是 Manifest 入面 <activity>android:launchMode 就搞到頭疼,後來更變成 Android 面試的經典題目。如果完全轉用 single activity 的做法的話基本上除了要做「一 app 多開」的效果外,基本上都不用特別處理 android:launchMode。「一 app 多開」的正式名稱是 task。意思是一個 app 可以在系統的「recent apps」顯示好幾個視窗,例如以前的 Chrome 開新 tab 都是用這個功能來做到。這個功能在 Word 這類應用非常合適,配合 split screen 來用就可以同時上下顯示兩個 Word 文件並同時編輯。

說回 navigation 的部分,AndroidX 的 Navigation component 除了處理換頁時的 Fragment 切換和 deep link 之外,還有是管理每頁傳入的參數、換頁動畫,配搭 Dagger Hilt 的話更可以設定某些 object 的 scope 是跟 navigation graph 共生死。

安裝 Navigation component

首先在 project 的 build.gradle 加入 safe args Gradle plugin:

dependencies {
     // ...
     classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
 }
@@ -135,8 +145,8 @@
     </style>
 </resources>
 

這篇的 code 有點長,我們下一篇會寫 navigation graph 的部分和把 MainActivity 改成顯示那個 navigation graph 的頁面。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-18-navigation-2/index.html b/2021/10/2021-ithome-ironman-18-navigation-2/index.html index 2cd8bbcd..04add363 100644 --- a/2021/10/2021-ithome-ironman-18-navigation-2/index.html +++ b/2021/10/2021-ithome-ironman-18-navigation-2/index.html @@ -1,5 +1,30 @@ 2021 iThome 鐵人賽 Day 18:Navigation (2) | EricLog -

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文

文章目錄

在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml

eta.xml 在 project 的位置

先附上完整的內容,然後再慢慢講解入面的意思。

<?xml version="1.0" encoding="utf-8"?>
+

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文

文章目錄

在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml

eta.xml 在 project 的位置

先附上完整的內容,然後再慢慢講解入面的意思。

<?xml version="1.0" encoding="utf-8"?>
 <navigation xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -114,8 +139,8 @@
     TKL("將軍澳綫", "Tseung Kwan O Line"),
 }
 

基本上我們凡是見到 XML 檔有 class 的引用都應該要留是把那些 class 剔除在混淆範圍之內。

小結

Navigation component 看似把以往處理一般轉頁和 deep link 的麻煩事變得更易管理,但它是不是真的那麼好用呢?當然不是!如果簡單看過它的文檔或許會覺得它很美好,但去到實際使用時就發覺有大大小小的問題,感覺它就是一個半製成品般。我強烈建議大家看看 Isaac Udy 演講的 Navigation in multi-module projects, and the problem with AndroidX Navigation,裏面有提及當 multi-module 時使用 Navigation component 所遇到的問題和解決方法,但那些解決方法都會令原本的 Navigation component 特色削弱(例如 type safe 的 parameter 變不 type safe),所以我才說 Navigation component 似是一個半製成品。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-19-station-list-screen-1/index.html b/2021/10/2021-ithome-ironman-19-station-list-screen-1/index.html index 73104347..772df6ca 100644 --- a/2021/10/2021-ithome-ironman-19-station-list-screen-1/index.html +++ b/2021/10/2021-ithome-ironman-19-station-list-screen-1/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 19:Station list screen (1) | EricLog -

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文

文章目錄

最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品:

車站列表 UI

這頁基本上就是一個 RecyclerView,當用戶點擊路綫時就會展開其車站,再點擊車站就會開啟抵站時間頁面。如果想做到縮放車站名的話,最簡單的方法就是當路綫名是一種 view type、車站名是另一種 view type,只需要準備好一個 List 交予 ListAdapter 讓它幫我們 render 就可以了。而路綫名旁邊的三角形 icon 會隨着車站名是否展開來決定顯示那一款 icon。這個亦可以在那個 List 時順帶提供給 ListAdapter 知道就可以了。

ListAdapterRecyclerView.Adapter 的 subclass,特色是它已經為我們準備了 AsyncListDiffer 計算更新列表時那些 list item 如何處理(例如更換現有項目、刪除項目還是中途插入一個新項目之類)和 submitList(List)(提交要顯示的 List)。我們只需要準備一個 DiffUtil.ItemCallback 和一些平時 RecyclerView.Adapter 都會做的東西(ViewHolder class、onCreateViewHoldergetItemViewTypeonBindViewHolder)就可以了,其餘那些 notifyDataSetChanged、在 RecyclerView.Adapter 準備一個 List field 來儲存現在顯示的內容之類我們都不用處理,因為 ListAdapter 已經幫我們做好了。

Dependency

由於我們開始實作 UI 的部分,lifecycle 和其他 UI 的 dependency 是不能缺的。

implementation "com.google.android.material:material:$materialVersion"
+

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文

文章目錄

最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品:

車站列表 UI

這頁基本上就是一個 RecyclerView,當用戶點擊路綫時就會展開其車站,再點擊車站就會開啟抵站時間頁面。如果想做到縮放車站名的話,最簡單的方法就是當路綫名是一種 view type、車站名是另一種 view type,只需要準備好一個 List 交予 ListAdapter 讓它幫我們 render 就可以了。而路綫名旁邊的三角形 icon 會隨着車站名是否展開來決定顯示那一款 icon。這個亦可以在那個 List 時順帶提供給 ListAdapter 知道就可以了。

ListAdapterRecyclerView.Adapter 的 subclass,特色是它已經為我們準備了 AsyncListDiffer 計算更新列表時那些 list item 如何處理(例如更換現有項目、刪除項目還是中途插入一個新項目之類)和 submitList(List)(提交要顯示的 List)。我們只需要準備一個 DiffUtil.ItemCallback 和一些平時 RecyclerView.Adapter 都會做的東西(ViewHolder class、onCreateViewHoldergetItemViewTypeonBindViewHolder)就可以了,其餘那些 notifyDataSetChanged、在 RecyclerView.Adapter 準備一個 List field 來儲存現在顯示的內容之類我們都不用處理,因為 ListAdapter 已經幫我們做好了。

Dependency

由於我們開始實作 UI 的部分,lifecycle 和其他 UI 的 dependency 是不能缺的。

implementation "com.google.android.material:material:$materialVersion"
 implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
 implementation "androidx.activity:activity-ktx:$activityKtxVersion"
 implementation "androidx.fragment:fragment-ktx:$fragmentKtxVersion"
@@ -229,8 +239,8 @@
     }
 }
 

現在 StationListAdapter 已經完成了,完整的 code 可以到 GitHub repo 查閱。下一篇會實作 ViewModel、Fragment 的部分,屆時就能完成車站列表頁的部分。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-20-station-list-screen-2/index.html b/2021/10/2021-ithome-ironman-20-station-list-screen-2/index.html index d7343643..a5bd20b8 100644 --- a/2021/10/2021-ithome-ironman-20-station-list-screen-2/index.html +++ b/2021/10/2021-ithome-ironman-20-station-list-screen-2/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 20:Station list screen (2) | EricLog -

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文

文章目錄

上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。

StationListViewModel

首先我們要寫的 class 是 StationListViewModel。首先來看看它的基本骨架:

@HiltViewModel
+

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文

文章目錄

上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。

StationListViewModel

首先我們要寫的 class 是 StationListViewModel。首先來看看它的基本骨架:

@HiltViewModel
 class StationListViewModel @Inject constructor(
     getLinesAndStations: GetLinesAndStationsUseCase,
 ) : ViewModel(), StationListAdapter.Callback {
@@ -159,8 +169,8 @@
     }
 }
 

小結

來到這裏車站列表頁已經完成了。本篇介紹了 ViewModel 的定位:提供 FlowFragment subscribe 來更新 UI 和提供 method 供 Fragment 通知 ViewModel 用戶做了甚麼動作,從而讓 ViewModel 執行適當的動作回應,例如用戶按下按鈕後會 call use case 並將新的狀態以 Flow 通知 Fragment。另外,我們用 Channel 做出 SingleLiveEvent 的效果。最後還介紹了 Navigation component 在轉頁時的陷阱。如果想對 ViewModel 的定位有更深入的了解可以看看「Don’t let ViewModel know about framework level dependencies」一文。

完整的 code 可以到 GitHub repo 查閱。下一篇我們會開始做抵站時間頁,屆時會有更多 ViewModelFlow 的示範。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-21-eta-screen-1/index.html b/2021/10/2021-ithome-ironman-21-eta-screen-1/index.html index 076b4c91..d2ac9051 100644 --- a/2021/10/2021-ithome-ironman-21-eta-screen-1/index.html +++ b/2021/10/2021-ithome-ironman-21-eta-screen-1/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 21:ETA screen (1) | EricLog -

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文

文章目錄

現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面:

抵站時間頁畫面流程

大致上可以分為兩部分:首次成功載入前和首次成功載入後。在首次成功載入前我們要全頁顯示載入中和各式錯誤畫面,但在首次成功載入後我們就盡量停留在班次畫面,如果是不能連接互聯網這類普通錯誤類型的話我們就在頁頂顯示一個 banner,下方就維持顯示之前成功載入的班次資料。但如果是出現事故和延誤的話那就不適宜顯示之前載入的班次,因為很可能都不正確,所以就全頁顯示錯誤。我們會在每次 call 完 API endpoint 後隔一段時間再 call 一次 API endpoint 更新內容,這樣用戶就不用刻意手動更新。

班次列表

現在我們就先實作 RecyclerView 的部分。做法其實跟之前都是差不多,還是分為行車方向和班次兩個 view type。

List item types

跟上次一樣,我們都是會建立供 adapter 專用的 sealed interface 來表示顯示的 list item。留意我們不會把上下行標題 String 直接放進去,因為要避免 configuration change 後仍然顯示未切換語言前的文字。另外,因為 Header 只有一個 property,我們可以轉用 value class。

Value class 以前是叫做 inline class,本身設計的用途是用來明確標明那個 parameter 的意義。Kotlin 的 Duration 本身都是 value class。在定義 value class 時是需要加上 @JvmInline。我們看看下面的例子,原本 getProduct 的參數是 Int,但 Int 的意義不夠明顯。我們可以開一個 value class ProductId 做這個 parameter 的 type。這樣要 call getProduct 就要特別地「instantiate」一個 ProductId,那用家就一定知道這個數字是 product ID 而不是 user ID 之類的東西。留意 value class 在 compile 的時候會盡量拆走那個 type,所以 compile 後的 getProduct 參數最終只會變成 Int,這樣就不用擔心額外的開銷。但如果你做了好幾個放 Int 的 value class 然後又用 when 去判斷那個是不是 ProductId 的話,Kotlin compiler 就只會把那些 variable 的 type 變回普通 Java class 般(因為不可能拿着幾個 Int variable 就可以分辨到是那個 value class)。

// 原本的寫法
+

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文

文章目錄

現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面:

抵站時間頁畫面流程

大致上可以分為兩部分:首次成功載入前和首次成功載入後。在首次成功載入前我們要全頁顯示載入中和各式錯誤畫面,但在首次成功載入後我們就盡量停留在班次畫面,如果是不能連接互聯網這類普通錯誤類型的話我們就在頁頂顯示一個 banner,下方就維持顯示之前成功載入的班次資料。但如果是出現事故和延誤的話那就不適宜顯示之前載入的班次,因為很可能都不正確,所以就全頁顯示錯誤。我們會在每次 call 完 API endpoint 後隔一段時間再 call 一次 API endpoint 更新內容,這樣用戶就不用刻意手動更新。

班次列表

現在我們就先實作 RecyclerView 的部分。做法其實跟之前都是差不多,還是分為行車方向和班次兩個 view type。

List item types

跟上次一樣,我們都是會建立供 adapter 專用的 sealed interface 來表示顯示的 list item。留意我們不會把上下行標題 String 直接放進去,因為要避免 configuration change 後仍然顯示未切換語言前的文字。另外,因為 Header 只有一個 property,我們可以轉用 value class。

Value class 以前是叫做 inline class,本身設計的用途是用來明確標明那個 parameter 的意義。Kotlin 的 Duration 本身都是 value class。在定義 value class 時是需要加上 @JvmInline。我們看看下面的例子,原本 getProduct 的參數是 Int,但 Int 的意義不夠明顯。我們可以開一個 value class ProductId 做這個 parameter 的 type。這樣要 call getProduct 就要特別地「instantiate」一個 ProductId,那用家就一定知道這個數字是 product ID 而不是 user ID 之類的東西。留意 value class 在 compile 的時候會盡量拆走那個 type,所以 compile 後的 getProduct 參數最終只會變成 Int,這樣就不用擔心額外的開銷。但如果你做了好幾個放 Int 的 value class 然後又用 when 去判斷那個是不是 ProductId 的話,Kotlin compiler 就只會把那些 variable 的 type 變回普通 Java class 般(因為不可能拿着幾個 Int variable 就可以分辨到是那個 value class)。

// 原本的寫法
 fun getProduct(productId: Int): Product
 
 // 用了 value class 的寫法
@@ -501,8 +511,8 @@
     }
 }
 

我們先看看 etaResult,我們用了 combineTransformlanguage(語言)、line(路綫)、station(車站)、sortedBy(排序)、triggerRefresh(觸發重新載入)整合在一起然後 call API endpoint(即是 GetEtaUseCase)。除了 triggerRefresh 之外,其餘的都是因為 GetEtaUseCase 需要用到才要加進入 combineTransform。而把 triggerRefresh 加進去只是單純想觸發它 call use case,這樣我們就可以一直使用同一個上游不用因為每次 call API 更新就要切換一個全新的 Flow。在 combineTransform 我們會用 emit 來發射最新的 value 去下游。在這裏我們先發射 Loadable.Loading 好讓我們在 UI 能顯示載入中的畫面。而之後的 emit 就會等待 getEta return 回來後才會發射實際結果。這個寫法會令每次 triggerRefresh 有東西被放進去後 etaResult 都會先發射載入中然後才發射實際 API 回傳結果。而最後轉成 StateFlow 就能讓下游(即是其餘在 data binding 用到的 StateFlowetaList)每次有人 collect 時都不用再 call endpoint。而在 init 我們為了一進入這頁就 call API,所以就加了一句 triggerRefresh.send(Unit) 來觸發第一次的 API call。

接着就是 loadedEtaResult,它只是用來方便寫之後的 Flow,因為有好幾個 Flow 都會載入後的值才能繼續。

然後就是顯示 RecyclerView 的部分。showEtaList 就是控制 RecyclerView 是不是可見,所以就要檢查是不是已收到 API 成功的結果。另外一個 FlowetaList,很明顯就是提供 RecyclerView 要顯示的內容。這個我們要取得 EtaResult.Success.schedule(班次)和 sortedBy(排序方式)來準備那個 List。在 combine 內有一個 sequence { ... }.toList() 的 block,其實我只是借 Sequence 來生成一個 List。因為 buildList 現在仍是 experimental,如果不想 opt-in 去用這些 API 的話就找了 sequence 來代替。跟上面的 combineTransform 有點似,sequence 都有 yield 來提供元素放進去 Sequence 內。整段 code 的大意是:如果是按方向排序的話,那就在方向開首加插一個標題(因為 EtaResult.Success.schedule 已經按方向排序好,所以我們會留意 EtaResult.Success.scheduledirection 跟上一筆是否不同就知道要不要加插標題);如果是按時間排序就直接生成 EtaListItem.Eta 就可以了。在轉換成供 adapter 使用的 EtaListItem.Eta 我們會把 Instant 換算成分鐘。那個 JavaDurationjava.time.Duration,幫它改名是因為之後我們會同時用到 Java 的 Duration 和 Kotlin 的 Duration,為免混淆我們就把它改名。

至於 goBack 就是處理用戶返回上一頁的動作,由於我們沒有特別的東西要做,所以就直接向 _navigateBack Channel 發送一個 Unit 就可以了,另外會提供一個 navigateBackFlowFragment 接收。同時做 private 的 Channel 跟 public 的 Flow 意義在於 Channel 本身就可以讓人放東西進去,在 ViewModel 我們應該只開放指定的渠道供 Fragment 去通知 ViewModel。把 Channel 直接 public 出去 Fragment 那邊就可以直接繞過我們原先設計的機制,所以在很多 ViewModel 的示範都會同時出現 MutableLiveDataLiveDataMutableStateFlowStateFlow 的組合,就是為了令 Fragment 那邊不能直接改變 value,要改變 value 就一定要經 ViewModel 指定 method 去改。習慣上如果出現這種組合的話,我們會把 private 那個 variable 前面加一個 underscore (_) 來分別 public 那一個。如果你能想到一個更好的命字就當然直接用另一個名字。分開 public/private 的好處是為日後功能有變動時留有空間,而且把 logic 保留在 ViewModel 內。

小結

這次的內容比較長,這是因為我想盡量壓縮篇數來寫其他內容。現在我們已經做了最基本顯示成功載入班次的部分。下一篇我們會暫時轉一轉題目,然後才繼續餘下的部分。

本篇我們看了用 combinecombineTransform 把多個 Flow 匯合成一個新的 Flow,以往用 LiveData 我們要自己 extend MediatorLiveData 才能做到的東西現在轉用 Flow 就有現成的東西可以用。

完整的 code 可以在 GitHub repo 找到,不過會夾雜本篇未完成的部分,希望大家不要介意。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-22-whistle-proxy/index.html b/2021/10/2021-ithome-ironman-22-whistle-proxy/index.html index c7689cbe..29fb6141 100644 --- a/2021/10/2021-ithome-ironman-22-whistle-proxy/index.html +++ b/2021/10/2021-ithome-ironman-22-whistle-proxy/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 22:Whistle proxy | EricLog -

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文

文章目錄

由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。

Proxy server 是我們常用的工具,我們能用 proxy server 檢查 app 的 HTTP request(這個之前介紹的 Flipper 都做到),亦能改變 response 令它變成我們想要的東西。這就可以令我們的 app 顯示到事故、延誤等畫面。這次我們會以 Whistle 作示範,特色是它是免費而且是跨平台都能使用。其實市面上有很多選擇,例如 CharlesProxymanFiddler 等等,用法都是大同小異。例如如何安裝根證書、系統或瀏覽器設定 proxy server 都是跟平台(例如 Android、iOS、Windows、macOS 等等)而不是跟 proxy server。而 proxy server 部分只需要知道其中一個 proxy server 檢視穿過 proxy server 的 HTTP traffic 以及何如改變 HTTP request 和 response 就應該很容易掌握到其他 proxy server 的用法。

安裝 Whistle proxy server

Whistle 是用 Node 寫的,所以要先在系統安裝 NodeNPM。如果你用 Node 比較多的話,建議經 NVM 安裝(POSIX 版Windows 版)。安裝 Whistle 的方法就是用 NPM 安裝:

npm install -g whistle
+

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文

文章目錄

由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。

Proxy server 是我們常用的工具,我們能用 proxy server 檢查 app 的 HTTP request(這個之前介紹的 Flipper 都做到),亦能改變 response 令它變成我們想要的東西。這就可以令我們的 app 顯示到事故、延誤等畫面。這次我們會以 Whistle 作示範,特色是它是免費而且是跨平台都能使用。其實市面上有很多選擇,例如 CharlesProxymanFiddler 等等,用法都是大同小異。例如如何安裝根證書、系統或瀏覽器設定 proxy server 都是跟平台(例如 Android、iOS、Windows、macOS 等等)而不是跟 proxy server。而 proxy server 部分只需要知道其中一個 proxy server 檢視穿過 proxy server 的 HTTP traffic 以及何如改變 HTTP request 和 response 就應該很容易掌握到其他 proxy server 的用法。

安裝 Whistle proxy server

Whistle 是用 Node 寫的,所以要先在系統安裝 NodeNPM。如果你用 Node 比較多的話,建議經 NVM 安裝(POSIX 版Windows 版)。安裝 Whistle 的方法就是用 NPM 安裝:

npm install -g whistle
 

啟動及終止 Whistle proxy server

安裝後,可以在 terminal 用 w2 指令啟動 Whistle:

PS C:\Users\Eric> w2 start
 [i] whistle@2.7.23 started
 [i] 1. use your device to visit the following URL list, gets the IP of the URL you can access:
@@ -69,8 +79,8 @@
     </base-config>
 </network-security-config>
 

這樣就可以在 debug 版的 app 用 proxy server 了。

小結

本篇示範了用 Whistle proxy server 做一些簡單的 request/response 處理和在 Android app 允許非系統預載的證書。這樣在下一篇繼續做班次頁的各式錯誤頁時就能用 proxy server 改變 response 來測試 app 的效果。此外,proxy server 是 frontend(包括 web 和 mobile)常用工具。了解 proxy server 基本用法能方便日常的開發(例如在陌生的 project 可以透過觀察 HTTP traffic 然後搜尋到對應的 code 位置)。Whistle 有太多功能,這次只是介紹了它的皮毛,詳細的用法還是需要參閱完整的文檔

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-23-eta-screen-2/index.html b/2021/10/2021-ithome-ironman-23-eta-screen-2/index.html index 606cd74c..6f03d24a 100644 --- a/2021/10/2021-ithome-ironman-23-eta-screen-2/index.html +++ b/2021/10/2021-ithome-ironman-23-eta-screen-2/index.html @@ -1,5 +1,17 @@ 2021 iThome 鐵人賽 Day 23:ETA screen (2) | EricLog -

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文

文章目錄

SavedStateHandle

不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。

大家看過不少有關 Architecture Components 講關於 ViewModel 的特色時都一定會提到 ViewModel 內的 variable 能在 configuration change 後都能保持着,因為 Activity 或者 Fragment 在 configuration change 後經 ViewModelProvider 拿到的 ViewModel 是之前的 instance,不像其他 View 要重新 instantiate 過,用了它就好像解決到大部分 Android 開發麻煩的問題。但有沒有考慮到如果裝置記憶體有限時要 kill app 然後用戶從 recent screen 開啟之前用過的 app 又會怎樣?很多人都忽略了這個環節,可能現在的裝置比以前多很多 RAM,少了用戶明顯為意到的 kill app 情況。但有時在一些舊裝置仍有可能發生。例如你的 app 用 activity result 打開了預設的相機 app 拍照,拍完就返回你的 app。但有可能在返回你的 app 那時整個 app 已經被系統殺掉而重新啟動,但因為你沒有特別處理這個情況導致拍照後的流程中斷了。

要處理這個問題,以往都是建議大家用 Activity/FragmentonSaveInstanceState callback 來儲存目前的 state 然後從 onCreate 或者 onRestoreInstanceState/onViewStateRestored callback 取回系統 kill app 前的 state。但現在有了 ViewModel 都會把 state 放入去而不是放在 Activity/Fragment,如果要把 ViewModel 的 state 交去 Activity/Fragment 路綫就會很迂迴。有見及此就出了 SavedStateHandleSavedStateHandle 是從 ViewModel 的 constructor 取得,可以經它存取 key value 組合,就像 Bundle 一樣 (savedStateHandle["xxx"])。但不同的是它除了取得 value 外,還可以取得 value 的 LiveData (savedStateHandle.getLiveData("xxx", "default value")),好讓你把 state 直接放入去 SavedStateHandle。這做法有別於以往,因為 ViewModel 沒有那些 onSaveInstanceStateonRestoreInstanceState callback。由於系統能隨時 kill app,所以就要把 SavedStateHandle 視作儲存當前 state 的地方,而不是待系統 kill app 前一刻才放資料進去。

SavedStateHandle 另一個用途是用來取得 Activity 的 intent extras 和 Fragment argument。獲取方式跟之前的 savedStateHandle["xxx"] 一樣("xxx" 是 intent extras/argument 的 key)。但我們已經用了 Navigation Component 的 Safe Args plugin,用 plugin 就是為了 Bundle 做到 type-safe,現在 SavedStateHandle 要走回頭路要自已寫 key 不覺得有點怪嗎?但其實是可以自己寫一個 delegate 將 SavedStateHandle 內儲存的 key-value pair 變成 Safe Args plugin 生成的 argument class 的 object。

@MainThread
+

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文

文章目錄

SavedStateHandle

不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。

大家看過不少有關 Architecture Components 講關於 ViewModel 的特色時都一定會提到 ViewModel 內的 variable 能在 configuration change 後都能保持着,因為 Activity 或者 Fragment 在 configuration change 後經 ViewModelProvider 拿到的 ViewModel 是之前的 instance,不像其他 View 要重新 instantiate 過,用了它就好像解決到大部分 Android 開發麻煩的問題。但有沒有考慮到如果裝置記憶體有限時要 kill app 然後用戶從 recent screen 開啟之前用過的 app 又會怎樣?很多人都忽略了這個環節,可能現在的裝置比以前多很多 RAM,少了用戶明顯為意到的 kill app 情況。但有時在一些舊裝置仍有可能發生。例如你的 app 用 activity result 打開了預設的相機 app 拍照,拍完就返回你的 app。但有可能在返回你的 app 那時整個 app 已經被系統殺掉而重新啟動,但因為你沒有特別處理這個情況導致拍照後的流程中斷了。

要處理這個問題,以往都是建議大家用 Activity/FragmentonSaveInstanceState callback 來儲存目前的 state 然後從 onCreate 或者 onRestoreInstanceState/onViewStateRestored callback 取回系統 kill app 前的 state。但現在有了 ViewModel 都會把 state 放入去而不是放在 Activity/Fragment,如果要把 ViewModel 的 state 交去 Activity/Fragment 路綫就會很迂迴。有見及此就出了 SavedStateHandleSavedStateHandle 是從 ViewModel 的 constructor 取得,可以經它存取 key value 組合,就像 Bundle 一樣 (savedStateHandle["xxx"])。但不同的是它除了取得 value 外,還可以取得 value 的 LiveData (savedStateHandle.getLiveData("xxx", "default value")),好讓你把 state 直接放入去 SavedStateHandle。這做法有別於以往,因為 ViewModel 沒有那些 onSaveInstanceStateonRestoreInstanceState callback。由於系統能隨時 kill app,所以就要把 SavedStateHandle 視作儲存當前 state 的地方,而不是待系統 kill app 前一刻才放資料進去。

SavedStateHandle 另一個用途是用來取得 Activity 的 intent extras 和 Fragment argument。獲取方式跟之前的 savedStateHandle["xxx"] 一樣("xxx" 是 intent extras/argument 的 key)。但我們已經用了 Navigation Component 的 Safe Args plugin,用 plugin 就是為了 Bundle 做到 type-safe,現在 SavedStateHandle 要走回頭路要自已寫 key 不覺得有點怪嗎?但其實是可以自己寫一個 delegate 將 SavedStateHandle 內儲存的 key-value pair 變成 Safe Args plugin 生成的 argument class 的 object。

@MainThread
 inline fun <reified Args : NavArgs> navArgs(savedStateHandle: SavedStateHandle) =
     NavArgsLazy(Args::class) {
         val pairs = savedStateHandle.keys()
@@ -150,8 +162,8 @@
     }
 }
 

留意不是所有 Android 裝置都有內置瀏覽器,為謹慎起見我們要 catch ActivityNotFoundException,並顯示 toast 提示用戶不能開啟瀏覽器。

小結

來到這裏應該可以順利地執行 app 並運用上篇介紹的 Whistle proxy server 造出不同的 response 來測試這頁。這篇我們討論了 SavedStateHandle 和避免在 layout XML 寫複雜 binding expression 的方法。完整的 code 可以在 GitHub repo 找到,下一篇我們會把這頁做成自動更新,不用先出去再進入班次頁才能看到最新內容。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-24-eta-screen-3/index.html b/2021/10/2021-ithome-ironman-24-eta-screen-3/index.html index 619b22ac..de46b6bf 100644 --- a/2021/10/2021-ithome-ironman-24-eta-screen-3/index.html +++ b/2021/10/2021-ithome-ironman-24-eta-screen-3/index.html @@ -1,5 +1,18 @@ 2021 iThome 鐵人賽 Day 24:ETA screen (3) | EricLog -

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文

文章目錄

我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。

我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值:

import kotlin.time.Duration as KotlinDuration
+

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文

文章目錄

我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。

我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值:

import kotlin.time.Duration as KotlinDuration
 
 private val AUTO_REFRESH_INTERVAL = KotlinDuration.seconds(10)
 

由於我們會用 Kotlin Coroutine 的 delay 來做延時的效果,它是用 Kotlin 的 Duration 作為參數,故此這裏就用 Kotlin 的 Duration。而因為之前我們用了 Java 的 Instant 來表示抵站時間,在換算站時間「X 分鐘」的數字時會用到 Java 的 Duration,為了避免混淆所以我們預先用 import alias 分開兩個 Duration

紀錄載入時間

我們做自動更新除了考慮用戶一直停留在該頁時能自動更新外,還要考慮 Android 的 lifecycle 問題。如果用戶在班次頁按 Home button 的話,我們應該暫停自動更新;而當用戶由其他 app 切換到該頁的時候就要回復自動更新。Fragment 的話就是要留意 onPauseonResume callback,在 onPause 時停止下次再 call API 的排程而在 onResume 重新 call API 一次。但如果用戶很快速地做 onPauseonResume 的話那可能會導致 call API 太密。所以我們應該在每次收到 response 時都記錄時間,然後在 onResume 檢查上次的載入的時間來決定要馬上 call API 還是要隔一會才 call API。這樣的話就做一個 data class 記錄那個時間:

private data class TimedValue<out T>(
@@ -146,8 +159,8 @@
         initialValue = TimedValue(value = Loadable.Loading, updatedAt = Instant.EPOCH),
     )
 

現在我們不把 triggerRefresh 放入 combineTransform/combine 內,改為以 triggerRefresh 觸發整個 combineflatMapLatest 就是把 lambda 內的 Flow 轉交去下游。flatMapLatest 內的 Flow 是把兩個 Flow(載入中和 call API 加下次更新排程)接駁為一個 Flow(用 flattenConcat)。因為用了 flattenConcat,所以載入中那個值會比 API response 那個值來得早,正正就是我們想要的效果。

小結

現在我們已經做了定時自動更新的功能了,亦都用了 custom scope 來停止之前的 delay,感覺有點像 RxJava 的 CompositeDisposable。另外亦經 Dagger inject Java Time 的 Clock 獲取當前時間而不是用 System.currentTimeMillis 或者 Calendar.getInstance() 獲取,這個做法是為了方便寫 unit test。至於實際如何寫 unit test 我們會待其餘功能完成後示範。下一篇會完成當成功載入班次後更新出現錯誤時會顯示的 banner,這次的 code 可以到 GitHub repo 找尋「Auto refresh」commit 就會找到。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-25-eta-screen-4/index.html b/2021/10/2021-ithome-ironman-25-eta-screen-4/index.html index e19a4429..45cbf421 100644 --- a/2021/10/2021-ithome-ironman-25-eta-screen-4/index.html +++ b/2021/10/2021-ithome-ironman-25-eta-screen-4/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 25:ETA screen (4) | EricLog -

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文

文章目錄

現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。

Layout XML

現在先看看 EtaFragment layout XML 的改動:

<?xml version="1.0" encoding="utf-8"?>
+

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文

文章目錄

現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。

Layout XML

現在先看看 EtaFragment layout XML 的改動:

<?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools">
@@ -224,8 +234,8 @@
     // 略
 }
 

小結

這樣我們就完成了顯示錯誤 banner 功能了。本篇主要是示範了用 scan operator 把當前和以前的值整合成一個 Flow 內,另外亦用 enum 表達目前整頁的狀態。其實我們不知不覺間已經將整頁的狀態由一個 StateFlow 表達出來(就是 etaResult),只不過我們另外衍生一堆零碎 StateFlow 供 data binding 用。其實我們可以將 etaResultscreenState 兩個 Flow 二合為一,屆時 ScreenState 不會是 enum 而是 sealed interface,原先的 LOADINGETAFULL_SCREEN_ERRORETA_WITH_ERROR_BANNER 就會變成一個個 object expression 和 data class。這樣就做到了 MVI (Model-View-Intent) 的 state reducer 風味的東西,而且又有類似 unidirectional data flow 的機制。現在那些 showLoadingshowFullScreenErrorshowEtaList 之類的 StateFlow 都是為了避免在 layout XML 寫太複雜的邏輯(因為不方便做 unit testing 和它本身是 XML 所以某些字符要 escape)而造出來的。日後改用 Jetpack Compose 寫 UI 的話相信可以減省到只外露 ScreenState sealed interface 的 Flow 就足夠了。

完整的 code 可以到 GitHub repo 查閱,下一篇我們會寫 ViewModel 的 unit test case。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-26-station-list-screen-testing/index.html b/2021/10/2021-ithome-ironman-26-station-list-screen-testing/index.html index 94169b06..c6b7f6d2 100644 --- a/2021/10/2021-ithome-ironman-26-station-list-screen-testing/index.html +++ b/2021/10/2021-ithome-ironman-26-station-list-screen-testing/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 26:Station list screen testing | EricLog -

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文

文章目錄

終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockKStrikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric

Robolectric

在 Android SDK 入面有不少 class 是跟 Java Standard Library 一樣,但亦都有一大堆 class 是 Android SDK 才會有,例子有 ContextUri 等等。由於 Java Standard Library 沒有這堆 class,如果無特別處理的話就不能在電腦上跑 unit test,只能拿到 Android 裝置上執行(實機或模擬器),所以就出現了 Robolectric 這個 library。它能令你在電腦上執行帶有 Android SDK 獨有 class 的 unit test,原理就是它會為每個 Android 版本都預備一個 JAR 檔案,入面載入那些 Android SDK 獨有 class 的 stub,好讓在執行 unit test 時不會找不到那些 class。當然你亦可以自己用 mock library 例如 MockK 把 Android SDK 的 class 都 mock 一次,但實際上要 mock 的話就很大機會不只要 mock 一個 class。例如 context.resources.getString 這個 method 你要先 mock Context 再 mock Resources 然後再 mock getResourcesgetString 兩個 method,所以還是用 Robolectric 比較實際。

順帶一提,正因為 Robolectric 是製造一堆跟 Android SDK 同 signature 的 class,所以當新的 Android 版本推出時不會馬上就有對應該 Android 版本的 JAR 可供下載,要等好幾個月才會有。

首先我們需要加入 Android 測試相關的 dependency:

testImplementation "androidx.test:core-ktx:$testCoreVersion"
+

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文

文章目錄

終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockKStrikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric

Robolectric

在 Android SDK 入面有不少 class 是跟 Java Standard Library 一樣,但亦都有一大堆 class 是 Android SDK 才會有,例子有 ContextUri 等等。由於 Java Standard Library 沒有這堆 class,如果無特別處理的話就不能在電腦上跑 unit test,只能拿到 Android 裝置上執行(實機或模擬器),所以就出現了 Robolectric 這個 library。它能令你在電腦上執行帶有 Android SDK 獨有 class 的 unit test,原理就是它會為每個 Android 版本都預備一個 JAR 檔案,入面載入那些 Android SDK 獨有 class 的 stub,好讓在執行 unit test 時不會找不到那些 class。當然你亦可以自己用 mock library 例如 MockK 把 Android SDK 的 class 都 mock 一次,但實際上要 mock 的話就很大機會不只要 mock 一個 class。例如 context.resources.getString 這個 method 你要先 mock Context 再 mock Resources 然後再 mock getResourcesgetString 兩個 method,所以還是用 Robolectric 比較實際。

順帶一提,正因為 Robolectric 是製造一堆跟 Android SDK 同 signature 的 class,所以當新的 Android 版本推出時不會馬上就有對應該 Android 版本的 JAR 可供下載,要等好幾個月才會有。

首先我們需要加入 Android 測試相關的 dependency:

testImplementation "androidx.test:core-ktx:$testCoreVersion"
 testImplementation "androidx.test.ext:junit:$testExtJunitVersion"
 testImplementation "androidx.arch.core:core-testing:$coreTestingVersion"
 testImplementation "org.robolectric:robolectric:$robolectricVersion"
@@ -196,8 +206,8 @@
     }
 }
 

這個 test 寫法很簡單,就是看看當 onClickLineAndStationlaunchEtaScreen 有沒有發射那個路綫和車站 Pair

小結

我們看過 Robolectric 的設定和示範了如何在 unit test 取得 Context。有了 Robolectric 我們就可以把一些不太跟 UI 有很大關係但又用了 Android SDK 的 class 的 code 在非 Android 裝置上執行 unit test。這樣可以加快 unit test 執行速度(因為在 Android 裝置執行 unit test 必定比在普通電腦上執行 unit test 慢)。另外又示範了改變 Main dispatcher 的方法和用 Turbine 幫助測試 Kotlin Flow。下一篇我們會開始寫 EtaViewModel 的 unit test。完整的 code 可以在 GitHub repo 找到。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/index.html b/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/index.html index 79050343..52673cd7 100644 --- a/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/index.html +++ b/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/index.html @@ -1,5 +1,20 @@ 2021 iThome 鐵人賽 Day 27:ETA screen testing (1) | EricLog -

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文

文章目錄

上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。

EtaPresenter

首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是:

  1. 提供一堆輸入和預期輸出值的 Collection(例如 List
  2. Constructor 的 parameter 會接收那些參數
  3. 在 test method 可以拿 constructor 的 parameter 來做測試的輸入和預期輸出值

因為這次要用 Robolectric 取得 Android 的 string resource,我們要先在 build.gradle 加入以下的東西才能令 Robolectric 取得 Android resource:

android {
+

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文

文章目錄

上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。

EtaPresenter

首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是:

  1. 提供一堆輸入和預期輸出值的 Collection(例如 List
  2. Constructor 的 parameter 會接收那些參數
  3. 在 test method 可以拿 constructor 的 parameter 來做測試的輸入和預期輸出值

因為這次要用 Robolectric 取得 Android 的 string resource,我們要先在 build.gradle 加入以下的東西才能令 Robolectric 取得 Android resource:

android {
     // 略……
     testOptions {
         unitTests {
@@ -240,8 +255,8 @@
     }
 }
 

小結

現在我們已經寫了幾個 test case,了解到如何測試 Flow,順帶介紹了 SharedFlow。另外亦在寫測試時發現先前寫的 code 有 bug,這其實是正常的,因為靠實機人手體驗可能會看不到一些問題,換了另一個角度又會看得到之前不為意的問題。下一篇我們會寫一些跟時間相關的 test case,完整的 code 可以在 GitHub repo 找到。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/index.html b/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/index.html index d479fc1b..2749968e 100644 --- a/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/index.html +++ b/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/index.html @@ -1,5 +1,18 @@ 2021 iThome 鐵人賽 Day 28:ETA screen testing (2) | EricLog -

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文

文章目錄

上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。

之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public:

import kotlin.time.Duration as KotlinDuration
+

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文

文章目錄

上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。

之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public:

import kotlin.time.Duration as KotlinDuration
 
 val AUTO_REFRESH_INTERVAL = KotlinDuration.seconds(10)
 

在看 test case 的 code 之前我們先了解 Kotlin Coroutine 如何處理時間相關的測試。如果有接觸過 RxJava 的話,要測試跟時間相關的 operator 就要用到 TestScheduleradvanceTimeBy method 把時間快進。Kotlin Coroutine 的作法都是差不多,寫法是在 runBlockingTest 內 call advanceTimeBy。之前我們在寫 Ktor client 的測試時因為找不到 Ktor client 如何自訂 Executor 或者 Dispatcher,所以惟有用了 runBlocking 而不是 runBlockingTestrunBlockingrunBlockingTest 的分別是 runBlocking 內如果加了 delay(Duration.seconds(10)) 的話那個 test case 就真的會在等十秒才執行 delay 的下一句;但 runBlockingTest 就會自動把這些 delay 快進,直到它發現已經進入閒置狀態。這樣就可以令 test case 執行速度加快,不用再乾等十秒。

回到我們的 EtaViewModel,我們在每次收到 getEtaUseCase 的回傳值就會在 onEach 內執行一句 delaydelay 之後就向 triggerRefresh Channel 發訊號觸發整串 etaResult 執行一遍,那之後又會再執行多次 onEach 的東西。整串 etaResult 就是一個無限循環,不會有閒置狀態。所以我們不能簡單地靠 runBlockingTest 自動快進功能來寫跟自動更新相關的 test case。那我們要做的就是手動把時間快進,然後在快進後檢查 Flow 的值。另一樣東西要留意的是我們在 startAutoRefresh 會比對上次 getEtaUseCase 回傳的時間和現在時間來決定要 delay 多久才 call 另一次 getEtaUseCase。這個部分牽涉到 EtaViewModel constructor 的 Clock。所以除了用 Coroutine test 的 advanceTimeBy 外,我們亦需要把 Clock 的時間同時快進,這樣才能正確地模擬現實情景。這亦都是我在上一篇特意引入 ThreeTen Extra 的原因。

為了更簡單地快進兩邊的時間,我們先準備一個 extension function:

import kotlin.time.Duration as KotlinDuration
@@ -473,8 +486,8 @@
         }
     }
 

小結

這次的 code 比較長,這是因為那些 use case 的 return value 本身都很長,加上每個值都要做 assertion,但 test case 的寫法來來去去都是差不多。本篇主要介紹了 Kotlin Coroutine 測試時如何快進時間,另外亦實際示範了為甚麼我們要用 Clock 來獲取當前時間。其餘的 test case 因為性質相近所以我就不再寫了,因為現在都可以示範到那些手法。而我們的班次示範 app 來到現在都大致上完結,下篇會再抽一些題目再討論一下。這次的 code 可以在 GitHub repo 找到。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-29-leftover-topics/index.html b/2021/10/2021-ithome-ironman-29-leftover-topics/index.html index 15305726..9efb2016 100644 --- a/2021/10/2021-ithome-ironman-29-leftover-topics/index.html +++ b/2021/10/2021-ithome-ironman-29-leftover-topics/index.html @@ -1,5 +1,15 @@ 2021 iThome 鐵人賽 Day 29:Leftover topics | EricLog -

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文

文章目錄

我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。

Two-way data binding

我們在示範 app 一直都是在用 one-way data binding,只要在 layout XML 加上 @{ ... } 就能用到 LiveDataStateFlow 的值,並且能在 LiveDataStateFlow 的值改動時自動更新 UI(要設定好 LifecycleOwner)。Two-way data binding 適合在一些用戶輸入的 UI 組件使用,例如 TextEditCheckBox 之類。寫法會是這樣:

<EditText
+

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文

文章目錄

我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。

Two-way data binding

我們在示範 app 一直都是在用 one-way data binding,只要在 layout XML 加上 @{ ... } 就能用到 LiveDataStateFlow 的值,並且能在 LiveDataStateFlow 的值改動時自動更新 UI(要設定好 LifecycleOwner)。Two-way data binding 適合在一些用戶輸入的 UI 組件使用,例如 TextEditCheckBox 之類。寫法會是這樣:

<EditText
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@={viewModel.query}" />
@@ -66,8 +76,8 @@
     fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
 }
 

以上的 code 可以在 GitHub repo 找到。

習慣上我們會在實際需要開 thread 的地方指明 dispatcher,例如 withContext(Dispatchers.IO) { ... } 包住讀寫檔案的 code。因為在那個位置是最清楚自己需要用那個 dispatcher。如果把指明 dispatcher 的工作放到跟實際操作很遠的位置(例如 Activity)的話調用的時候就要額外花時間檢查那些 code 是不是要用其他 dispatcher 執行,在 Android Developers 的文檔亦建議 suspending function 應寫成能安全地在 main thread 上調用。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2021/10/2021-ithome-ironman-30-wrapping-up/index.html b/2021/10/2021-ithome-ironman-30-wrapping-up/index.html index 14155da1..5598caca 100644 --- a/2021/10/2021-ithome-ironman-30-wrapping-up/index.html +++ b/2021/10/2021-ithome-ironman-30-wrapping-up/index.html @@ -1,7 +1,17 @@ 2021 iThome 鐵人賽 Day 30:Wrapping up | EricLog -

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文

文章目錄

終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。

現在回顧那個示範 project 有個地方做錯的是改變排序不應該觸發 API call。要做到這個效果的話可以把排序的動作搬到 ViewModel 做,或者是 use case 是有自己的狀態(即是會保留之前取得的 response)。前者的話就令到 domain layer 是多餘;後者的話我又覺得班次這件事不會有其他東西觸發它改變,把它的狀態保留又有點怪。如果我們做的功能是類似 Facebook news feed,這樣 domain layer 保留一份資料就很合理。因為可以在 domain layer 整合 push notification,當有 push notification 的時候就用 push payload 更新本地現有的 news feed,然後把 news feed 外露成 Flow。這樣 ViewModel 就不用知道有甚麼情況那個 news feed 會被更新,只需要訂閱 news feed 就可以了。

如果把排序的 logic 放到 ViewModel,這樣 use case 就只是左手交右手(把 data layer 的東西轉交予 presentation layer)。但我好難找到一個有充實 business logic 而且做完之後又可以抄到我自己的 side project 用的情景(因為選即時班次是因為我的 side project MetroRide app 要做這個功能)。其實不是沒有,只是 business logic 太複雜,講解它都用不少篇幅,變相整個系列不是在示範 Android 的東西。如果個別 use case 沒有甚麼特別的 logic 的話,為了令整個 app 的做法統一還是建議寫 use case

另外一個做得不好的地方是倒數分鐘只是靠每十秒 call API 後更新而不是按實際時間更新。解決方法可以是再另加一個每秒觸發的 Flow 來重新計算倒數分鐘值,但因為我不想弄得太複雜而沒有做到(因為寫完又要示範寫 test case,本身每十秒自動更新都要寫一大堆 test case)。

如果要談完成鐵人賽有甚麼東西學到的話,我想主要是試用了一次 Ktor client、Kotlin Coroutine scope 和 Flow 測試快進時間。以前做的項目都是用 OkHttp 加 Retrofit,如果是要測試快進時間的話都是用 RxJava。Kotlin Coroutine 和 Flow 感覺上比 RxJava 易學,最起碼沒有 subscribeOnobserveOn 傻傻分不清楚的問題,而且 Coroutine 的 suspending function 可以令整個寫法看起來像平常寫 code 的形式,不用費神想如何把它們串聯成一條 RxJava 的 Single/Completable 之類。加上現在 Android Jetpack 都有足夠的配套,像是 lifecycle 的 scope、data binding 支援等等。用過之後就回不了 LiveData 和 RxJava 去。

整個系列有三分之一是做測試,這是我特別花篇幅做的。因為身邊不少同事都不太有寫自動化測試的經驗,亦不明白為甚麼要用 dependency injection library。我覺得沒有寫過自動化測試的人是很難明白為甚麼要弄一大堆 interface,又要用 constructor injection。因為這些東西本來就可以用 singleton 就能做到。但如果你了解到寫測試時需要 everything’s under control 的話就自然明白到為甚麼要搞這麼多東西,為的就是能在測試時控制到自己想要的情景。還有是揀選 architecture 時可以用能否容易寫 unit test 做參考準則,如果發覺很難寫或者寫得很古怪的話那個 architecture 應該都有點問題。現在有了 Dagger Hilt,Android Jetpack 又有配套,就算不完全明白全部 Dagger 的東西也可以輕鬆做到 dependency injection。

最後,感謝各位花時間閱讀我的文章。如果想看其他的東西可以到我的 blog,如果有用 Medium 的話可以 follow 我的 Medium


順帶一提,有一個有趣的東西是我在做示範 app 的時候無意中發現港鐵的抵站時間 API 居然會時光倒流。最初看到 UI 還以為我計算倒數出錯所以顯示不到倒數,但檢查 API response 才發現他們 backend 的班次不會把日期進位,一直停留在同一日,直至全部班次都是翌日才會輸出正確的日期。我想他們應該是把日期和時間分開處理,之後臨到輸出 response 時才把它們合併一起。

港鐵的抵站時間 API 班次時間計算錯誤
comments powered by Disqus
© 2024 EricLog +

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文

文章目錄

終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。

現在回顧那個示範 project 有個地方做錯的是改變排序不應該觸發 API call。要做到這個效果的話可以把排序的動作搬到 ViewModel 做,或者是 use case 是有自己的狀態(即是會保留之前取得的 response)。前者的話就令到 domain layer 是多餘;後者的話我又覺得班次這件事不會有其他東西觸發它改變,把它的狀態保留又有點怪。如果我們做的功能是類似 Facebook news feed,這樣 domain layer 保留一份資料就很合理。因為可以在 domain layer 整合 push notification,當有 push notification 的時候就用 push payload 更新本地現有的 news feed,然後把 news feed 外露成 Flow。這樣 ViewModel 就不用知道有甚麼情況那個 news feed 會被更新,只需要訂閱 news feed 就可以了。

如果把排序的 logic 放到 ViewModel,這樣 use case 就只是左手交右手(把 data layer 的東西轉交予 presentation layer)。但我好難找到一個有充實 business logic 而且做完之後又可以抄到我自己的 side project 用的情景(因為選即時班次是因為我的 side project MetroRide app 要做這個功能)。其實不是沒有,只是 business logic 太複雜,講解它都用不少篇幅,變相整個系列不是在示範 Android 的東西。如果個別 use case 沒有甚麼特別的 logic 的話,為了令整個 app 的做法統一還是建議寫 use case

另外一個做得不好的地方是倒數分鐘只是靠每十秒 call API 後更新而不是按實際時間更新。解決方法可以是再另加一個每秒觸發的 Flow 來重新計算倒數分鐘值,但因為我不想弄得太複雜而沒有做到(因為寫完又要示範寫 test case,本身每十秒自動更新都要寫一大堆 test case)。

如果要談完成鐵人賽有甚麼東西學到的話,我想主要是試用了一次 Ktor client、Kotlin Coroutine scope 和 Flow 測試快進時間。以前做的項目都是用 OkHttp 加 Retrofit,如果是要測試快進時間的話都是用 RxJava。Kotlin Coroutine 和 Flow 感覺上比 RxJava 易學,最起碼沒有 subscribeOnobserveOn 傻傻分不清楚的問題,而且 Coroutine 的 suspending function 可以令整個寫法看起來像平常寫 code 的形式,不用費神想如何把它們串聯成一條 RxJava 的 Single/Completable 之類。加上現在 Android Jetpack 都有足夠的配套,像是 lifecycle 的 scope、data binding 支援等等。用過之後就回不了 LiveData 和 RxJava 去。

整個系列有三分之一是做測試,這是我特別花篇幅做的。因為身邊不少同事都不太有寫自動化測試的經驗,亦不明白為甚麼要用 dependency injection library。我覺得沒有寫過自動化測試的人是很難明白為甚麼要弄一大堆 interface,又要用 constructor injection。因為這些東西本來就可以用 singleton 就能做到。但如果你了解到寫測試時需要 everything’s under control 的話就自然明白到為甚麼要搞這麼多東西,為的就是能在測試時控制到自己想要的情景。還有是揀選 architecture 時可以用能否容易寫 unit test 做參考準則,如果發覺很難寫或者寫得很古怪的話那個 architecture 應該都有點問題。現在有了 Dagger Hilt,Android Jetpack 又有配套,就算不完全明白全部 Dagger 的東西也可以輕鬆做到 dependency injection。

最後,感謝各位花時間閱讀我的文章。如果想看其他的東西可以到我的 blog,如果有用 Medium 的話可以 follow 我的 Medium


順帶一提,有一個有趣的東西是我在做示範 app 的時候無意中發現港鐵的抵站時間 API 居然會時光倒流。最初看到 UI 還以為我計算倒數出錯所以顯示不到倒數,但檢查 API response 才發現他們 backend 的班次不會把日期進位,一直停留在同一日,直至全部班次都是翌日才會輸出正確的日期。我想他們應該是把日期和時間分開處理,之後臨到輸出 response 時才把它們合併一起。

港鐵的抵站時間 API 班次時間計算錯誤
comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/2022/01/androidx-room-relational-query-method/index.html b/2022/01/androidx-room-relational-query-method/index.html index 5be927d0..ed77ee3a 100644 --- a/2022/01/androidx-room-relational-query-method/index.html +++ b/2022/01/androidx-room-relational-query-method/index.html @@ -1,5 +1,5 @@ AndroidX Room Relational Query Method | EricLog -

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 intermediate data class)。

Relational Query Method

看過他們的 YouTube 影片,這個功能的用法很簡單。就是在平常的 DAO method 的 return type 改成 Map 就可以了。

@Query(
+

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 intermediate data class)。

Relational Query Method

看過他們的 YouTube 影片,這個功能的用法很簡單。就是在平常的 DAO method 的 return type 改成 Map 就可以了。

@Query(
     """
     SELECT rail_stations.*, rail_lines.* FROM rail_stations
     INNER JOIN rail_line_station ON rail_stations.id = rail_line_station.rail_station_id
@@ -131,8 +131,8 @@
     arg("room.schemaLocation", "$projectDir/schemas")
 }
 

總結

準備 database schema 時要留意將來 join table column 撞名問題。以前用過其他 backend 的 ORM library 它們都會為你處理好這些問題。其實這個問題本來是可以在 SQL statement 為 column 名補上 alias 解決,不過用 Room 都是為了方便,如果效能不是差太遠的話相信大家都不會特別為每句 query 都做一個專用的 @Entity data class。

另一樣要留意的地方是 auto migration 有時候都不能做到你心目中的效果。在這次的清況如果真的要做 migration 的話可能要分幾次進行,確保順序是合乎自己的期望。另一個做法是把 annotation processor 生成出來的 code 抄去手動 migration class 內。

至於轉用 KSP 有沒有變快其實比較難感受到,因為 module 還有 Dagger Hilt 要用 KAPT,所以未能完全感受到 KSP 的速度。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2022/07/jetpack-compose-migration-1/index.html b/2022/07/jetpack-compose-migration-1/index.html index 16519ee3..17a0a6f0 100644 --- a/2022/07/jetpack-compose-migration-1/index.html +++ b/2022/07/jetpack-compose-migration-1/index.html @@ -1,5 +1,5 @@ Jetpack Compose 遷移 (1) | EricLog -

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 Fragment 內的 onCreateView 加入 setContent 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。

遷移方式

由於我本身打算一次過把全個 app 的 UI 都換成 Compose,所以除了先前的延誤定義頁之外我就沒有再特別去把原有的 Fragment 淘空 layout XML,而是直接造一個全新的 MainActivity 來顯示全新製造的 Compose UI,舊有的 layout XML 和 Fragment 就盡量不去改,方便遷移時能拿來對照參考。當全部頁面都轉成 Compose 後就會刪除舊的 Fragment 和 layout XML。另外,這個 app 本身是 single Activity app,所以只有單一 Activity,遷移後亦都會維持這個做法。

除了大改的列車抵站時間部分,原有頁面的 ViewModel 和背後的 code 都不會大改,因為這次目的是想轉用 Compose 和補元列車抵站時間功能。現有 code 在 ViewModel 和 UI 之間的通訊方式是以 LiveDataViewModel 的 function,再加上使用 databinding。在遷移現有頁面時 ViewModel 要改動的地方不算太多,而背後拿資料的部分都不用改。

狀態

Compose 和傳統 view system 其中一個明顯差別就是狀態處理。Compose 特別強調狀態,明確地分別出 composable function 的參數、rememberrememberSavableremember 就是在 Compose rerender (recompose) 時能保存的 state,而 rememberSavable 就更進一步連 process 被 kill 後重開 app 都能保存 state。即是跟 savedInstanceState 一樣,只是重新包裝方便將 Compose 帶去非 Android 平台用。

官方建議盡量寫 stateless 的 composable function,把 state 都推去上層管理(正式叫法是 state hoisting),你可以把 state 推到上去 ViewModel,即是跟傳統 Activity/Fragment 般一個頁面配一個 ViewModel 來管理 state。但不代表要把所有 state 都推到去 ViewModel 管理。一般建議的做法是比較關 UI 事的 state 就保留在 composable function 內放置(靠 rememberrememberSavable),例如 scroll、animation 的 state。而偏向資料性的 state 就交由 ViewModel 管理。目前我都在摸索除了那些很明顯的 scroll、animation 之類的 UI state 外,還有甚麼 state 是應該放去 Compose 那邊。以顯示列車班次為例,UI 要顯示班次抵站的倒數時間。究竟應該把定時計算倒數分鐘的 code 放去非 Compose 的地方,然後經 ViewModelStateFlow 傳去要顯示分鐘的 composable function 讓它直接拿那個預先計算好的數字顯示出來還是 ViewModel 只向 composable function 提供絕對時間(Instant 之類),composable function 會定時計算倒數分鐘並顯示出來(會有 side-effect)。這次我選了前者,但下次再選的話可能會選後者。我沒有太仔細看官方的 sample project,但似乎因為現在 UI 的部分可以直接寫 Kotlin code,不像以往 XML layout 般難把 Java/Kotlin code 塞入去(databinding 都是 Java syntax 而且不能分行寫),現在可以把偏 UI 相關的 code 都放去 composable function 內。但因為這次沒有寫 UI test,反而是要刪走以前針對傳統 view system 寫的 UI test,所以還未能拿捏到應不應該把這類的 logic 下放到 composable function 內。

這次因為遷移時還保留着現有的 Fragment 和 layout XML,所以不太想修改現有的 ViewModel 以免動到舊有的 code。我之前在舊公司試過新頁面用 Compose 寫,在寫 code 時其實是會自然地傾向把 composable function 寫到 stateless 和減少 logic,最好連 UI 顯示式樣都由非 Compose 的地方計算好。以 WhatsApp 的訊息列表為例(見下圖),會以 sealed interface/class 的形式向 UI 表示每間房間的最新訊息欄的式樣(自己發送單剔、自己發送雙剔、別人發送、房間沒有訊息等等),然後 composable function 放個 when 來實際控制 UI 顯示式樣(例如顏色、icon),而不是在 composable function 內拿到個未處理的 object 再在 composable function 內寫一堆 code 去決定顯示那款最新訊息式樣。同時亦會慢慢傾向造一個 StateFlow 去表達整頁的 state。如果要用 data class 並以 immutable 的方式表達整頁的 state 的話,在複雜的環境下可能需要借助 kotlinx.collections.immutableArrow Optics 之類的 library 來生成新的 state object,因為那些 state 可能放到很深入,用 data class copy function 不夠方便,會很累贅。

WhatsApp 最新訊息欄

另一樣應該都算關 state 事的地方是 Compose 對那些要費時完成的動作(例如 scroll 去某個位置、彈出 bottom sheet 之類)的 function 都定義為 suspending function。以往在傳統 view system 可能只是 call 完 function 就算,但 Compose 就要你開一個 coroutine scope 才能 call。

由於是另起爐灶,Navigation 我沒有做甚麼遷移,要做的東西就是重新做過一個新的 NavHost。如果是分階段遷移的話一般都是叫你用 Fragment 來顯示 composable function 並沿用現有的 XML 式 navigation graph 直頭全部頁面都換成 Compose 才轉用 Compose 版 Navigation。Compose 版 Navigation 跟 XML 明顯分別是沒有 IDE 預覽、轉用 route(string 形式)而不是用 R class ID 和沒有 SafeArgs,而傳遞參數都改以 query parameter 形式而不是用 Bundle。很多人都不習慣這個新設定,有人用 Kotlin Symbol Processing (KSP) 還原了 SafeArgs 功能。官方就建議大家寫 sealed class 來表達 route。我覺得 Compose Navigation 應該比 XML 版 Navigation 更容易做到 runtime 隨時切換 navigation graph(例如用戶登入前和登入後),過份追求跟 XML 版 Navigation 功能一致可能會令隨時切換 navigation graph 變得更難做到。另一樣比較多人不喜歡的地方是傳遞參數要用 query parameter。我初初以為 route 是像 RESTful 般如果有子目錄就是代表是某一個 route 的子頁 (sub-graph),但實際試用過才發現那些 route 就只是一個名字而已。而不能傳 Parcelable 參數是因為官方建議是應傳 ID 之類的簡單參數,開了另一頁後才憑那個 ID 從 local storage(SQLite database、Data Store、SharedPreferences、file system)找回那個大 object。以前工作遇過有人會用 singleton object 來暫存那些大 object,因為太大不能塞進去 intent extras。但這個做法沒有考慮到系統 kill process 後用戶從 Recents screen 再次打開 app 的情景,所以還是應該把大 object 放去 local storage。

另外,route 其實跟 deep link 沒有關連,因為 deep link 是另外宣告的,但 deeplink 用到的 argument 要在 arguments 一併宣告。

composable(
+

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 Fragment 內的 onCreateView 加入 setContent 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。

遷移方式

由於我本身打算一次過把全個 app 的 UI 都換成 Compose,所以除了先前的延誤定義頁之外我就沒有再特別去把原有的 Fragment 淘空 layout XML,而是直接造一個全新的 MainActivity 來顯示全新製造的 Compose UI,舊有的 layout XML 和 Fragment 就盡量不去改,方便遷移時能拿來對照參考。當全部頁面都轉成 Compose 後就會刪除舊的 Fragment 和 layout XML。另外,這個 app 本身是 single Activity app,所以只有單一 Activity,遷移後亦都會維持這個做法。

除了大改的列車抵站時間部分,原有頁面的 ViewModel 和背後的 code 都不會大改,因為這次目的是想轉用 Compose 和補元列車抵站時間功能。現有 code 在 ViewModel 和 UI 之間的通訊方式是以 LiveDataViewModel 的 function,再加上使用 databinding。在遷移現有頁面時 ViewModel 要改動的地方不算太多,而背後拿資料的部分都不用改。

狀態

Compose 和傳統 view system 其中一個明顯差別就是狀態處理。Compose 特別強調狀態,明確地分別出 composable function 的參數、rememberrememberSavableremember 就是在 Compose rerender (recompose) 時能保存的 state,而 rememberSavable 就更進一步連 process 被 kill 後重開 app 都能保存 state。即是跟 savedInstanceState 一樣,只是重新包裝方便將 Compose 帶去非 Android 平台用。

官方建議盡量寫 stateless 的 composable function,把 state 都推去上層管理(正式叫法是 state hoisting),你可以把 state 推到上去 ViewModel,即是跟傳統 Activity/Fragment 般一個頁面配一個 ViewModel 來管理 state。但不代表要把所有 state 都推到去 ViewModel 管理。一般建議的做法是比較關 UI 事的 state 就保留在 composable function 內放置(靠 rememberrememberSavable),例如 scroll、animation 的 state。而偏向資料性的 state 就交由 ViewModel 管理。目前我都在摸索除了那些很明顯的 scroll、animation 之類的 UI state 外,還有甚麼 state 是應該放去 Compose 那邊。以顯示列車班次為例,UI 要顯示班次抵站的倒數時間。究竟應該把定時計算倒數分鐘的 code 放去非 Compose 的地方,然後經 ViewModelStateFlow 傳去要顯示分鐘的 composable function 讓它直接拿那個預先計算好的數字顯示出來還是 ViewModel 只向 composable function 提供絕對時間(Instant 之類),composable function 會定時計算倒數分鐘並顯示出來(會有 side-effect)。這次我選了前者,但下次再選的話可能會選後者。我沒有太仔細看官方的 sample project,但似乎因為現在 UI 的部分可以直接寫 Kotlin code,不像以往 XML layout 般難把 Java/Kotlin code 塞入去(databinding 都是 Java syntax 而且不能分行寫),現在可以把偏 UI 相關的 code 都放去 composable function 內。但因為這次沒有寫 UI test,反而是要刪走以前針對傳統 view system 寫的 UI test,所以還未能拿捏到應不應該把這類的 logic 下放到 composable function 內。

這次因為遷移時還保留着現有的 Fragment 和 layout XML,所以不太想修改現有的 ViewModel 以免動到舊有的 code。我之前在舊公司試過新頁面用 Compose 寫,在寫 code 時其實是會自然地傾向把 composable function 寫到 stateless 和減少 logic,最好連 UI 顯示式樣都由非 Compose 的地方計算好。以 WhatsApp 的訊息列表為例(見下圖),會以 sealed interface/class 的形式向 UI 表示每間房間的最新訊息欄的式樣(自己發送單剔、自己發送雙剔、別人發送、房間沒有訊息等等),然後 composable function 放個 when 來實際控制 UI 顯示式樣(例如顏色、icon),而不是在 composable function 內拿到個未處理的 object 再在 composable function 內寫一堆 code 去決定顯示那款最新訊息式樣。同時亦會慢慢傾向造一個 StateFlow 去表達整頁的 state。如果要用 data class 並以 immutable 的方式表達整頁的 state 的話,在複雜的環境下可能需要借助 kotlinx.collections.immutableArrow Optics 之類的 library 來生成新的 state object,因為那些 state 可能放到很深入,用 data class copy function 不夠方便,會很累贅。

WhatsApp 最新訊息欄

另一樣應該都算關 state 事的地方是 Compose 對那些要費時完成的動作(例如 scroll 去某個位置、彈出 bottom sheet 之類)的 function 都定義為 suspending function。以往在傳統 view system 可能只是 call 完 function 就算,但 Compose 就要你開一個 coroutine scope 才能 call。

由於是另起爐灶,Navigation 我沒有做甚麼遷移,要做的東西就是重新做過一個新的 NavHost。如果是分階段遷移的話一般都是叫你用 Fragment 來顯示 composable function 並沿用現有的 XML 式 navigation graph 直頭全部頁面都換成 Compose 才轉用 Compose 版 Navigation。Compose 版 Navigation 跟 XML 明顯分別是沒有 IDE 預覽、轉用 route(string 形式)而不是用 R class ID 和沒有 SafeArgs,而傳遞參數都改以 query parameter 形式而不是用 Bundle。很多人都不習慣這個新設定,有人用 Kotlin Symbol Processing (KSP) 還原了 SafeArgs 功能。官方就建議大家寫 sealed class 來表達 route。我覺得 Compose Navigation 應該比 XML 版 Navigation 更容易做到 runtime 隨時切換 navigation graph(例如用戶登入前和登入後),過份追求跟 XML 版 Navigation 功能一致可能會令隨時切換 navigation graph 變得更難做到。另一樣比較多人不喜歡的地方是傳遞參數要用 query parameter。我初初以為 route 是像 RESTful 般如果有子目錄就是代表是某一個 route 的子頁 (sub-graph),但實際試用過才發現那些 route 就只是一個名字而已。而不能傳 Parcelable 參數是因為官方建議是應傳 ID 之類的簡單參數,開了另一頁後才憑那個 ID 從 local storage(SQLite database、Data Store、SharedPreferences、file system)找回那個大 object。以前工作遇過有人會用 singleton object 來暫存那些大 object,因為太大不能塞進去 intent extras。但這個做法沒有考慮到系統 kill process 後用戶從 Recents screen 再次打開 app 的情景,所以還是應該把大 object 放去 local storage。

另外,route 其實跟 deep link 沒有關連,因為 deep link 是另外宣告的,但 deeplink 用到的 argument 要在 arguments 一併宣告。

composable(
     route = "stationDetail/{id}",
     arguments = listOf(
         navArgument("id") {
@@ -102,8 +102,8 @@
     }
 }
 

小結

其實實際的遷移工作不用做這麼久,只是因為這是 side project 沒有時間限制所以才慢慢做。這次先分享到這裏,我本來想一併分享其餘內容,但都是留待下篇才寫。下次應該會寫 interop 的部分。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2022/07/jetpack-compose-migration-2/index.html b/2022/07/jetpack-compose-migration-2/index.html index 0525c86f..84b831e1 100644 --- a/2022/07/jetpack-compose-migration-2/index.html +++ b/2022/07/jetpack-compose-migration-2/index.html @@ -1,5 +1,10 @@ Jetpack Compose 遷移 (2) | EricLog -

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。

Dependency injection

按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 Activity 或者 Fragment。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式:

  1. ViewModel 的 constructor injection 拿到 dependency,然後用 Hilt 的 viewModelhiltViewModel function 在 composable function 拿到 ViewModel instance,再從 ViewModel 拿到 dependency
  2. Activity 或者 Fragment@Inject 做 field injection,然後帶進去 setContent lambda
  3. 為 dependency 準備 entry pointContext 可以經 LocalContext.current 拿到

其實三個方法我覺得都是很不 Compose。但很多時都可以避免到在 composable function 拿 dependency。例如在 ViewModel 外露已經計算好結果的 StateFlow,composable function 就會接到 data class 形式的 state,不需接觸到其他 dependency。這樣做 preview function 都容易處理。Android platform 的 dependency 例如 system service 可以用 LocalContext.current 拿到 Context 之後就拿到。我目前是按照官方建議 screen level 的 composable function 可以拿到 ViewModel,但內裏用到的 composable function 部件就不會再接觸到 ViewModel。但即使限制 composable function 接觸到 ViewModel 的機會,但始終會有部分 composable function 因為 parameter 有 ViewModel 而不能寫 preview function。日後我會試試把 ViewModel 限制在 Navigation component 的 composable/dialog lambda 內使用,所有 StateFlow 全部在 navigation 那邊轉成 Compose 的 StateViewModel function call 變成 callback lambda。這樣就更容易為 screen level 的 composable function 寫 preview function。因為現在沒有 XML 版 Navigation component 這種的圖像化 navigation graph,如果連 screen level 的 composable function 都無預覽的話去到大 project 就很難維護。

除了那三種方法外,如果是比較通用的東西可以用 CompositionLocal 而不用逐層傳入,例如剛才提及的 LocalContext。可以理解 CompositionLocal 就是 service locator。其實 Compose 的 Material Design theming 都用了 CompositionLocal 來減少傳遞參數。例子有 LocalContentAlpha 用來改變某個範圍的顏色透明度。我在這次遷移應該算是用得克制,只加了兩個自訂 CompositionLocal:一個是用來提供 Java 的 Clock,另一個是用來拿用戶語言設定(因為 app 有自己的語言設定,不是按系統設定),方便顯示文字內容。用法大致上是這樣:

@Composable
+

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。

Dependency injection

按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 Activity 或者 Fragment。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式:

  1. ViewModel 的 constructor injection 拿到 dependency,然後用 Hilt 的 viewModelhiltViewModel function 在 composable function 拿到 ViewModel instance,再從 ViewModel 拿到 dependency
  2. Activity 或者 Fragment@Inject 做 field injection,然後帶進去 setContent lambda
  3. 為 dependency 準備 entry pointContext 可以經 LocalContext.current 拿到

其實三個方法我覺得都是很不 Compose。但很多時都可以避免到在 composable function 拿 dependency。例如在 ViewModel 外露已經計算好結果的 StateFlow,composable function 就會接到 data class 形式的 state,不需接觸到其他 dependency。這樣做 preview function 都容易處理。Android platform 的 dependency 例如 system service 可以用 LocalContext.current 拿到 Context 之後就拿到。我目前是按照官方建議 screen level 的 composable function 可以拿到 ViewModel,但內裏用到的 composable function 部件就不會再接觸到 ViewModel。但即使限制 composable function 接觸到 ViewModel 的機會,但始終會有部分 composable function 因為 parameter 有 ViewModel 而不能寫 preview function。日後我會試試把 ViewModel 限制在 Navigation component 的 composable/dialog lambda 內使用,所有 StateFlow 全部在 navigation 那邊轉成 Compose 的 StateViewModel function call 變成 callback lambda。這樣就更容易為 screen level 的 composable function 寫 preview function。因為現在沒有 XML 版 Navigation component 這種的圖像化 navigation graph,如果連 screen level 的 composable function 都無預覽的話去到大 project 就很難維護。

除了那三種方法外,如果是比較通用的東西可以用 CompositionLocal 而不用逐層傳入,例如剛才提及的 LocalContext。可以理解 CompositionLocal 就是 service locator。其實 Compose 的 Material Design theming 都用了 CompositionLocal 來減少傳遞參數。例子有 LocalContentAlpha 用來改變某個範圍的顏色透明度。我在這次遷移應該算是用得克制,只加了兩個自訂 CompositionLocal:一個是用來提供 Java 的 Clock,另一個是用來拿用戶語言設定(因為 app 有自己的語言設定,不是按系統設定),方便顯示文字內容。用法大致上是這樣:

@Composable
 fun MetroRideAppContainer(
     viewModel: MainViewModel,
     splashScreenVisibleCondition: (SplashScreen.KeepOnScreenCondition) -> Unit,
@@ -27,8 +32,8 @@
     }
 }
 

Interop

MetroRide app 如果講跟傳統 view system 之間的 interop 的話就只有 AdMob 的 AdView 需要用到,Composable function 的 code 我已經放到 Gist。但 LeakCanary 有時會投訴 AdMob 的 WebView 會有 memory leak。暫時找不到解決方法。另外亦為 IDE preview 做了個專用 UI,方法是檢查 LocalInspectionModecurrent

AdMob composable function preview

至於其他非傳統 view system 之間的 interop,其實 Google 之前都出了一堆 library 讓你在用 Compose 時減少直接接觸 Android platform 的功能。例如 Jetpack Lifecycle-aware components 將 Activity lifecycle 抽離、Activity Result APIs 取代 startActivityForResultSavedStateHandle 取代 onSaveInstanceState 等等其實都會在 Compose 上用到。在 AdMob 那個 Gist 已經透過 LifecycleEventObserver 來得知當前的 lifecycle 狀態。

主題

Jetpack Compose 有提供 Material Design 2 和 3 的 artifact。目前我仍使用 2,因為 3 還在 alpha,組件未夠齊(即使 Compose 版 Material Design 2 都是比傳統 view system 版少組件)。但講自訂程度我覺得 Compose 比傳統 view system 更好。傳統 view system 有 style resource 但要花時間查有那些地方可以用 style 改動,但 Compose 可以自己查 Material Design 組件的 composable function 源碼,發覺有地方不能自訂就把它的 code 抄走再改。而且 Compose 部分組件有提供原始版 composable function,例如負責文字輸入的 BasicTextField,它就是單純提供文字輸入功能。你喜歡加標題可以自己另外加個 Text、框線等等。但如果是傳統 view system 你要抄走某個 class 的 code 都不容易,因為要兼顧 Java/Kotlin code 的 access modifier 和 XML styleable attribute。

MetroRide 的 UI 因為都是按照 Material Design 2 而造,所以沒有做太多自訂主題。最多都是按照 Material Design 2 做了 dialog(Compose 版 Material Design 2 做出來 dialog 跟 Material Design 網站講的式樣不同,所以我特別處理過)和搜尋欄。但在舊公司用 Compose 開發新功能時因為那個 app 的主題跟 Material Design 有出入所以試過自訂過 UI。大概是寫一個 composable function 包住 Compose 本身提供的組件 composable function,但已經把需要調整的參數都設定好,又或者是現有的組件被其他新組件包住。這個是 Compose 做自訂 UI 組件的方法:composition。

小結

Compose 遷移就寫到這裏。其實遷移工作斷斷續續地做了半年。因為是 side-project 沒有時間限制,做了一部分又放下來做其他東西,再加上重鐵和輕鐵抵站時間在很久之前做了出來(重鐵那個在上去年參加 iThome 鐵人賽時拿來做示範 app,輕鐵是 MetroRide app 之前已經有的功能),隔了一段時間後都忘了之前的 code 寫了甚麼。其實還想分享一下 Navigation component,不過我覺得可以再開多一篇文。所以這部分會在下一篇出現。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2022/07/jetpack-compose-navigation-component-sub-graph/index.html b/2022/07/jetpack-compose-navigation-component-sub-graph/index.html index 4bd4016d..186a6d64 100644 --- a/2022/07/jetpack-compose-navigation-component-sub-graph/index.html +++ b/2022/07/jetpack-compose-navigation-component-sub-graph/index.html @@ -1,5 +1,5 @@ Jetpack Compose Navigation component sub-graph | EricLog -

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 nested navigation graph 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。

接下來會用例子示範 nested graph 在 navigation 時出現的不同效果。首先放上在 NavHost 各頁會用到的 Screen composable function,它會顯示文字和按鈕,用來顯示當前頁面的 route 和按下按鈕時會轉到另一頁。

@Composable
+

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 nested navigation graph 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。

接下來會用例子示範 nested graph 在 navigation 時出現的不同效果。首先放上在 NavHost 各頁會用到的 Screen composable function,它會顯示文字和按鈕,用來顯示當前頁面的 route 和按下按鈕時會轉到另一頁。

@Composable
 fun Screen(
     name: String,
     onClick: (() -> Unit)? = null,
@@ -75,8 +75,8 @@
     )
 }
 

所以 sub-graph 的作用是在 deep linking 時控制 back stack,這種寫法是確保 Navigation component 能掌握該頁的上一頁,並且只有一頁能做上一頁(簡單來說是退路)。如果不是 deep linking 的話其實你可以隨便轉去整個 navigation graph 內的任何一頁,只要你知道它的 route 就可以了。另外,deep linking 不一定要配 URI(要在 AndroidManifest.xml 加上 intent-filter 那種),剛才例子的寫法是不用加 intent-filter。如果你要在某個很深層的頁面跳轉去另一個深層的頁面,用這個寫法會比每次特意控制 pop back stack 更加自然。當然,如果你是用 URI 那種 deep linking 的話,sub-graph 就是控制 deep link 開 app 後所造出來的 back stack。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2023/01/android-13-per-app-language-preferences/index.html b/2023/01/android-13-per-app-language-preferences/index.html index 884afe1e..fc0361e7 100644 --- a/2023/01/android-13-per-app-language-preferences/index.html +++ b/2023/01/android-13-per-app-language-preferences/index.html @@ -1,5 +1,5 @@ Android 13 Per-app Language Preferences | EricLog -

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。

App 內置的語言設定

這個功能相信大家都期待已久,尤其是香港的 app developer。可能以前 Google 那邊的人不太明白為甚麼要有這個功能,因為通常系統語言跟個別 app 語言都是一致。然而,香港用戶會有特別要求。例如不少用戶都把系統語言設定成英文、日文之類,但去到個別 app 例如新聞、地圖那類 app 又想用中文顯示(即使 app 界面不是用中文但仍想文章、地名之類用中文)。

這個功能的用法是在系統設定內選取系統 > 語言及輸入 > 應用程式語言中可以針對不同的 app 個別設置語言,不用跟系統語言。而同樣設定亦可在系統設定中的個別 app 的設定頁找到(應用程式 > 你的 app > 語言)。

這次官方介紹影片和網頁都講得夠清楚,有提及適配步驟和邊緣案例。大致上就是加 Appcompat library,在 manifest 加上要支援的 locale 和 AppLocalesMetadataHolderService,最後就是用 Appcompat 的 getApplicationLocalessetApplicationLocales 去讀取和設定語言。

系統設定內的「應用程式語言」頁

看起上來似乎很簡單,但實際上又不是。有部分東西 Android developers 沒有寫到。

基本適配方法

首先在在 XML resource directory (src/main/res/xml) 建立 locale_config.xml 指明你的 app 支援那些語言:

<?xml version="1.0" encoding="utf-8"?>
+

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。

App 內置的語言設定

這個功能相信大家都期待已久,尤其是香港的 app developer。可能以前 Google 那邊的人不太明白為甚麼要有這個功能,因為通常系統語言跟個別 app 語言都是一致。然而,香港用戶會有特別要求。例如不少用戶都把系統語言設定成英文、日文之類,但去到個別 app 例如新聞、地圖那類 app 又想用中文顯示(即使 app 界面不是用中文但仍想文章、地名之類用中文)。

這個功能的用法是在系統設定內選取系統 > 語言及輸入 > 應用程式語言中可以針對不同的 app 個別設置語言,不用跟系統語言。而同樣設定亦可在系統設定中的個別 app 的設定頁找到(應用程式 > 你的 app > 語言)。

這次官方介紹影片和網頁都講得夠清楚,有提及適配步驟和邊緣案例。大致上就是加 Appcompat library,在 manifest 加上要支援的 locale 和 AppLocalesMetadataHolderService,最後就是用 Appcompat 的 getApplicationLocalessetApplicationLocales 去讀取和設定語言。

系統設定內的「應用程式語言」頁

看起上來似乎很簡單,但實際上又不是。有部分東西 Android developers 沒有寫到。

基本適配方法

首先在在 XML resource directory (src/main/res/xml) 建立 locale_config.xml 指明你的 app 支援那些語言:

<?xml version="1.0" encoding="utf-8"?>
 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
     <locale android:name="en" />
     <locale android:name="zh-Hant-HK" />
@@ -48,8 +48,8 @@
     )
 }
 
系統設定內的「應用程式語言」頁和 app 本身的設定頁

舊 Android 版本未能成功切換 app 界面語言

MetroRide 本身支援 Android 5 或以上,正當我以為已經試完沒有問題的時候多手試試 API level 21 時效果如何。試過後發現原來經 app 內的 UI 切換語言後 UI 的 string resource 沒有切換成功。然後 Compose UI 有部分都爛掉。可能 Appcompat 只支援 Android 7 或以上。(因為系統語言排序是 Android 7 才開始有,而那些強行改 context wrapper 的 code 都是會特別針對 Android 7 或以上做處理。我沒有 API level 21 至 26 之間逐個去試。)

所以最後決定把 minSdk 升到 API 26 (Android 8.0),反正都沒有用戶用這麼舊的 Android 版本。

另一個有可能出事的位置是 WebView因為 WebView 內的 Google Chrome 會干擾 app locale。我沒有特意去試這個位置,因為我用的是 Custom Tabs 而不是 WebView

小結

其實這個功能都算易用,麻煩的地方主要是因為設定值要交由系統管理而不是直接由自己管理,所以就要加 broadcast receiver 之類的 code。好處方面很明顯就是 Appcompat 幫你做了以往那些 attachBaseContext 的東西不用再自己做,令 code 乾淨了不少。而新 API 用法都算簡單,跟之前切換 night mode 的 setDefaultNightMode() 有點像。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2023/07/android-pseudolocale/index.html b/2023/07/android-pseudolocale/index.html index 8c721630..50c616f3 100644 --- a/2023/07/android-pseudolocale/index.html +++ b/2023/07/android-pseudolocale/index.html @@ -1,5 +1,5 @@ Android pseudolocale | EricLog -

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。

Android SDK 其實有一個功能叫 pseudolocale,字面意思是假的地區設定。Android emulator 內置支援兩款 pseudolocale:en-XAar-XB,分別對應左至右和右至左語系。兩款 pseudolocale 都是用你的 app 的預設 string resource 加工而成。

XA 就是把預設的 string resource 的英文字母換成有變音符號的字母再在最尾加上 one two three 之類的文字去加長整段字,作用是為了模擬 app UI 在較長的左至右語言的情況,看看會不會塞爆 UI;而 XB 就是把預設的 string resource 的英文字母反轉次序,同時亦會令界面變成右至左模式,這樣就可以模擬 app UI 在右至左語言的情況。由於兩個 pseudolocale 都是由預設的 string resource 衍生出來(理論是預設是用來放英文),所以加工後你都應該能看得懂,不會因為切換了 pseudolocale 而不懂得操作自己的 app。

要啟用 pseudolocale,首先要在 project 的 build.gradle.kts 加入 resourceConfigurations 和在 build type 加入 isPseudoLocalesEnabled = true

android {
+

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。

Android SDK 其實有一個功能叫 pseudolocale,字面意思是假的地區設定。Android emulator 內置支援兩款 pseudolocale:en-XAar-XB,分別對應左至右和右至左語系。兩款 pseudolocale 都是用你的 app 的預設 string resource 加工而成。

XA 就是把預設的 string resource 的英文字母換成有變音符號的字母再在最尾加上 one two three 之類的文字去加長整段字,作用是為了模擬 app UI 在較長的左至右語言的情況,看看會不會塞爆 UI;而 XB 就是把預設的 string resource 的英文字母反轉次序,同時亦會令界面變成右至左模式,這樣就可以模擬 app UI 在右至左語言的情況。由於兩個 pseudolocale 都是由預設的 string resource 衍生出來(理論是預設是用來放英文),所以加工後你都應該能看得懂,不會因為切換了 pseudolocale 而不懂得操作自己的 app。

要啟用 pseudolocale,首先要在 project 的 build.gradle.kts 加入 resourceConfigurations 和在 build type 加入 isPseudoLocalesEnabled = true

android {
     defaultConfig {
         applicationId = "net.swiftzer.metroride"
         versionCode = 37
@@ -39,8 +39,8 @@
 }
 

不應改變的 string resource

由於 pseudolocale 會改變你的 string resource 內容,如果某些 string resource 被改動的話會導致 app crash,令你不能看到 UI。這種情況通常會在日期時間格式出現。要避免因為開了 pseudolocale 而令到 app crash,可以在相關的 string resource 加入 translatable="false",這樣在開了 pseudolocale 後這些 string resource 都不會被改變。

<string name="mtr_alert_timestamp_format_12hr" translatable="false">E, dd MMM h:mm a (zzz)</string>
 

在 Android Studio 會警告你翻譯了不應翻譯的 string resource,但不影響 build app。

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2024/02/android-14-migration/index.html b/2024/02/android-14-migration/index.html index f755ff93..1fe030a2 100644 --- a/2024/02/android-14-migration/index.html +++ b/2024/02/android-14-migration/index.html @@ -1,5 +1,10 @@ Android 14 migration | EricLog -

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。

Foreground service

MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission FOREGROUND_SERVICE_DATA_SYNC 標明 foreground service 的目的。

不過去到最後還是把 WorkManager 的 foreground service 抽走。原因是因為如果 target API level 34 而 manifest 又有加到 foreground service permission 的話在 Google Play 上架時要提供影片解釋每個 foreground service 的用法。可能是因為太多 app 用不能清掉的 notification 賣廣告唯有出此下策。為了這個 data sync 拍片解釋實在太煩,所以把它拿掉就算了。

Google Play Core

另一個要改的地方是 Play Core。本身在開 app 時有用 Play Core 的 in-app update 檢查有沒有更新,如果有的話就會提示用戶更新 app。本身一直都是用 com.google.android.play:core:1.10.3com.google.android.play:core-ktx:1.8.1 這兩個 artifact,而且是最新版。直到提交了 app 去 Play Store 做 pre-launch report 才知道有 taget API level 34 會 app crash,那時我還不以為然把它上架。最後才發現原來在 Android 14 的裝置會一開 app 即死。

Stack trace 大概是這樣:

Fatal Exception: java.lang.SecurityException: net.swiftzer.metroride: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
+

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。

Foreground service

MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission FOREGROUND_SERVICE_DATA_SYNC 標明 foreground service 的目的。

不過去到最後還是把 WorkManager 的 foreground service 抽走。原因是因為如果 target API level 34 而 manifest 又有加到 foreground service permission 的話在 Google Play 上架時要提供影片解釋每個 foreground service 的用法。可能是因為太多 app 用不能清掉的 notification 賣廣告唯有出此下策。為了這個 data sync 拍片解釋實在太煩,所以把它拿掉就算了。

Google Play Core

另一個要改的地方是 Play Core。本身在開 app 時有用 Play Core 的 in-app update 檢查有沒有更新,如果有的話就會提示用戶更新 app。本身一直都是用 com.google.android.play:core:1.10.3com.google.android.play:core-ktx:1.8.1 這兩個 artifact,而且是最新版。直到提交了 app 去 Play Store 做 pre-launch report 才知道有 taget API level 34 會 app crash,那時我還不以為然把它上架。最後才發現原來在 Android 14 的裝置會一開 app 即死。

Stack trace 大概是這樣:

Fatal Exception: java.lang.SecurityException: net.swiftzer.metroride: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
        at android.os.Parcel.createExceptionOrNull(Parcel.java:3069)
        at android.os.Parcel.createException(Parcel.java:3053)
        at android.os.Parcel.readException(Parcel.java:3036)
@@ -45,8 +50,8 @@
     // 其他 code
 }
 

Google Play recovery tools

交了新的 app 版本後,無意中發現原來 Google Play Console 現在可以做到 force update。效果跟 in-app update 差不多,但不用改 code 就做到 force update 效果。不過在我這個清況發揮不到作用,因為它一開 app 就 crash,似乎不夠時間讓它顯示 force update 的 UI。

大致上就是這樣,因為我的 app 功能比較少,所以都沒什麼特別的東西要改。

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2024/02/android-webview/index.html b/2024/02/android-webview/index.html index 3a125b6b..777b8296 100644 --- a/2024/02/android-webview/index.html +++ b/2024/02/android-webview/index.html @@ -1,5 +1,10 @@ Android WebView 筆記 | EricLog -

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。

AndroidX WebKit

AndroidX 其實有 WebKit 的 artifact androidx.webkit:webkit,但不是把整個 browser 加到 app 入面(WebView 實際在用的 web browser 是由 Google Play Store 提供,並可以 developer options 切換),而是把部分較新的 WebView 功能加個檢查 function,如果目前的 WebView 支援那個功能的話就可以執行這部分的 code。

AndroidX WebKit 入面有 WebViewClientCompat,這是用來 polyfill 部分 Android SDK 內的 WebViewClient。相信最值得一提的地方是 shouldOverrideUrlLoading。這個 method 是用來控制 WebView 是否載入這個網址。你可以在這個 callback 內截停某些網址載入,然後做其他東西。比如因應某些網址來 startActivity 讓那些網址改為系統瀏覽器開啟。另一個經典用法是用作由 JavaScript 傳遞資料給 native app,但如果經 URL 傳遞很長的 string 的話(例如 base64 encode 的圖片)很容易會 lag 機。

留意 WebViewClientCompatshouldOverrideUrlLoading 跟 Android SDK 那個行為不同:WebViewClientCompat 會把除 loadUrl 的頁面都改由系統預設瀏覽器開啟。

Lifecycle

WebView 是一個比較麻煩的 UI 組件,因為要特別手動接駁 Android Activity/Fragment 的 lifecycle,如果做漏的話 WebView 的 state 就會在 configuration change 後出錯。就好似 Google Maps SDK 的 MapView 一樣需要 forward lifecycle method。如果是 Activity 的話大概是這樣:

class WebViewActivity : AppCompatActivity() {
+

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。

AndroidX WebKit

AndroidX 其實有 WebKit 的 artifact androidx.webkit:webkit,但不是把整個 browser 加到 app 入面(WebView 實際在用的 web browser 是由 Google Play Store 提供,並可以 developer options 切換),而是把部分較新的 WebView 功能加個檢查 function,如果目前的 WebView 支援那個功能的話就可以執行這部分的 code。

AndroidX WebKit 入面有 WebViewClientCompat,這是用來 polyfill 部分 Android SDK 內的 WebViewClient。相信最值得一提的地方是 shouldOverrideUrlLoading。這個 method 是用來控制 WebView 是否載入這個網址。你可以在這個 callback 內截停某些網址載入,然後做其他東西。比如因應某些網址來 startActivity 讓那些網址改為系統瀏覽器開啟。另一個經典用法是用作由 JavaScript 傳遞資料給 native app,但如果經 URL 傳遞很長的 string 的話(例如 base64 encode 的圖片)很容易會 lag 機。

留意 WebViewClientCompatshouldOverrideUrlLoading 跟 Android SDK 那個行為不同:WebViewClientCompat 會把除 loadUrl 的頁面都改由系統預設瀏覽器開啟。

Lifecycle

WebView 是一個比較麻煩的 UI 組件,因為要特別手動接駁 Android Activity/Fragment 的 lifecycle,如果做漏的話 WebView 的 state 就會在 configuration change 後出錯。就好似 Google Maps SDK 的 MapView 一樣需要 forward lifecycle method。如果是 Activity 的話大概是這樣:

class WebViewActivity : AppCompatActivity() {
     private lateinit var webView: WebView
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -174,8 +179,8 @@
     }
 }
 

參考

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2024/06/firebase-cloud-messaging-legacy-api/index.html b/2024/06/firebase-cloud-messaging-legacy-api/index.html index d3ec278e..8ba9faf0 100644 --- a/2024/06/firebase-cloud-messaging-legacy-api/index.html +++ b/2024/06/firebase-cloud-messaging-legacy-api/index.html @@ -1,5 +1,5 @@ Firebase Cloud Messaging legacy API | EricLog -

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API

這個改動主要改變了拿 API token 的方法,以前是直接用 Firebase console 提供的 token 就能 call 那些 endpoint,但現在變成要先拿到 service account 的 JSON 檔案,之後再用 Google 提供的 Auth Library 生成有效期較短的 OAuth 2.0 access token 才能 call 到新的 endpoint。

在 6 月 21 日之後如果要發送 FCM 推送的話(例如想測試自己的 app 能不能正常處理推送),最簡單的方法是用 Firebase Admin SDK 發送推送。

首先要去 Firebase 的 project settings 頁,在 Firebase Admin SDK 分頁會找到「Generate new private key」按鈕,按一下這個按鈕。

Project settings

之後會彈出一個警告,再按「Generate key」。

警告

按下之後就能下載到一個 JSON 檔案。

之後就可以用 Firebase Admin SDK 來發送通知,以下會用 Java 版的 Admin SDK 及 Kotlin 示範:

val googleCredentials = GoogleCredentials
+

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API

這個改動主要改變了拿 API token 的方法,以前是直接用 Firebase console 提供的 token 就能 call 那些 endpoint,但現在變成要先拿到 service account 的 JSON 檔案,之後再用 Google 提供的 Auth Library 生成有效期較短的 OAuth 2.0 access token 才能 call 到新的 endpoint。

在 6 月 21 日之後如果要發送 FCM 推送的話(例如想測試自己的 app 能不能正常處理推送),最簡單的方法是用 Firebase Admin SDK 發送推送。

首先要去 Firebase 的 project settings 頁,在 Firebase Admin SDK 分頁會找到「Generate new private key」按鈕,按一下這個按鈕。

Project settings

之後會彈出一個警告,再按「Generate key」。

警告

按下之後就能下載到一個 JSON 檔案。

之後就可以用 Firebase Admin SDK 來發送通知,以下會用 Java 版的 Admin SDK 及 Kotlin 示範:

val googleCredentials = GoogleCredentials
     .fromStream(FileInputStream("credentials.json"))
     .createScoped("https://www.googleapis.com/auth/firebase.messaging")
 val firebaseOptions = FirebaseOptions.builder()
@@ -21,8 +21,8 @@
 googleCredentials.refresh()
 val accessToken = googleCredentials.accessToken.tokenValue
 

Authorization header 的值是 "Bearer $accessToken"

comments powered by Disqus
comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/2024/08/androidx-navigation-compose-type-safety/index.html b/2024/08/androidx-navigation-compose-type-safety/index.html index 09534dff..686d14fe 100644 --- a/2024/08/androidx-navigation-compose-type-safety/index.html +++ b/2024/08/androidx-navigation-compose-type-safety/index.html @@ -1,5 +1,5 @@ AndroidX Navigation component for Jetpack Compose type safety | EricLog -

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 Fragment、打開 Fragment 時要什麼參數和各 Fragment 之間如何導覽的 XML 檔案)。如果加上 Safe Args Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 Fragment 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。

AndroidX Navigation 有推出了 Compose 版,其實核心都是沿用原本處理 Fragment navigation 的那些 code。但每頁的「代號」由 Android resource ID 改為網址式的 string 表示(叫做 route)。轉頁在 AndroidX Navigation Compose 的理解就是按網址找到對應的 composable function 去 call 然後在網址抽取打開該頁要用到的參數。用法大概就是這樣:

composable("shop/{shopId}?promoCode={promoCode}") { backStackEntry ->
+

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 Fragment、打開 Fragment 時要什麼參數和各 Fragment 之間如何導覽的 XML 檔案)。如果加上 Safe Args Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 Fragment 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。

AndroidX Navigation 有推出了 Compose 版,其實核心都是沿用原本處理 Fragment navigation 的那些 code。但每頁的「代號」由 Android resource ID 改為網址式的 string 表示(叫做 route)。轉頁在 AndroidX Navigation Compose 的理解就是按網址找到對應的 composable function 去 call 然後在網址抽取打開該頁要用到的參數。用法大概就是這樣:

composable("shop/{shopId}?promoCode={promoCode}") { backStackEntry ->
     val shopId: String? = backStackEntry.arguments?.getString("shopId")
     val promoCode: String? = backStackEntry.arguments?.getString("promoCode")
 
@@ -146,8 +146,8 @@
 

這樣就可以直接開到 MetroRide 的設定頁。相信這對需要寫 end-to-end test 的人是個好消息,是一個 feature 而不是 bug。但如果你的 app 有部分頁面是需要登入後才能看的話這就比較尷尬(如果你不是在每頁都加登入檢查的話),因為這個方法可以繞過登入頁。

其實能夠打開到設定頁的原因是因為 NavController 會拿 Activity.intent 去檢查 graph 內有沒有頁面能對應到這個 deep link(Activity.intent.data 就是 deep link URI),而那些由 Navigation component 自行生成的 route(即是 android-app://androidx.navigation/ 開首的網址)都會被 match 到。結果就能中門大開隨意進入 NavHost 內任何一頁。

NavController 那一句 handleDeepLink

但似乎 Google 未修正這個問題,如果想馬上解決的話除了不用 Navigation component 之外就是把 Intent.data 清走。

ActivityonCreate(在 setContent 前)加入這個 if

if (intent.data?.scheme == "android-app" && intent.data?.authority == "androidx.navigation") {
     intent.data = null
 }
-

如果 Activity 本身有設定 launch mode 是 singleTop 的話,亦應在 onNewIntent 刪走 Intent.data

這篇文章其實有示範到其中一頁有個 WebViewWebView 載入的網址是從開那頁的參數提供並且那個 WebView 會在 request header 加入 token,結果透過這個方法就能拿到 token。這個其實應該在每次加入 token 前就要檢查一次 request URL 是不是指定的 URL 才加入 token 的 request header。如果想加強 JavaScript 與 native app 雙向溝通安全性的話可以參考 Android WebView 筆記

結語

我覺得很多 Android/iOS developer 在做 navigation 時沒有像那些 backend framework 處理 request 的思維。一般那些 backend framework 在處理 request/response 時都會經過一些 middleware,例如你可以定義一個 middleware 用來檢查用戶是否已登入,然後把它套在需要登入後才能進入的 route。這個 middleware 的大概內容是如果沒有登入就重定向到登入頁,然後直接 output response,不用再交去下一個 middleware 或者是該 route 對應的 controller。這其實跟 OkHttp 的 interceptor 相似。背後的 design pattern 是 Chain of Responsibility,是 Gang of Four Design Patterns 內的 pattern。

Google 將登入後才能進入否則重定向稱為 conditional navigation。它建議先讓用戶進入受限頁面,然後再用 Activity 層級開一個 UserViewModel,入面決定用戶是否已登入。而在受限頁面的 onViewCreated 就 observe UserViewModel 外露的是否已登入 LiveData。當發現未登入就馬上重定向到登入頁(準碓來說是在在受限頁面上顯示登入頁,沒有清除 back stack)。正因為沒有清除 back stack,用戶可以在登入頁按返回鍵回到之前的受限頁面。為了防止不斷重定向,它在受限頁面的 SavedStateHandle 加了一個 boolean flag 表明用戶是否成功登入,然後在受限頁面檢查那個 boolean flag 防止不斷在兩頁之間重定向。當然,今時今日不會有人這樣寫,因為兩個 ViewModel 之間通訊是很麻煩的事。通常都是用 dependency injection graph 加上一個能共用 instance 的 class 然後在入面放那些是否已登入的 Flow/LiveData/Observable。但 Google 這個提議最大問題是當頁面一多就很難 scale,因為它要求每頁都要加檢查的 code。

如果我們沿用 AndroidX Navigation component 又想做到類似 middleware 的效果,大概就是弄一個 suspending function 包起 navController.navigate,在那個 suspending function 內檢查要 navigate 的 route 有沒有 middleware 要執行,有的話就逐個執行。如果最終確認是可以直接進入目標頁面那就可以執行 navController.navigate。如果是要重定向的話就要為重定向頁做同樣的 middleware 執行動作,直到找到最終目的地為止。而用 suspending function 的原因是那些 middleware 可能需要做 I/O 動作,不像 navController.navigate 可以馬上完成。而 NavHost 外面要加一個載入畫面,當執行 middleware 時就蓋在 NavHost 上面不能讓用戶按到畫面其他地方。這樣就不用每一頁都要再檢查一次用戶是否已登入,因為在進入前已經被 middleware 檢查過。

很多 navigation library 偏向是補強 AndroidX Navigation component 欠缺生成 boilerplat code 的功能、或者令整個 app 變得更 MVI。反而中國出品的 navigation library 會做這類接近 middleware 的東西,但不知道有沒有考慮非同步的問題。它們甚至能做到由 backend 控制 navigation 的目的地(例如因應 A/B testing、feature flag 的值在 navigation 時開啟不同頁面)。

參考

comments powered by Disqus

如果 Activity 本身有設定 launch mode 是 singleTop 的話,亦應在 onNewIntent 刪走 Intent.data

這篇文章其實有示範到其中一頁有個 WebViewWebView 載入的網址是從開那頁的參數提供並且那個 WebView 會在 request header 加入 token,結果透過這個方法就能拿到 token。這個其實應該在每次加入 token 前就要檢查一次 request URL 是不是指定的 URL 才加入 token 的 request header。如果想加強 JavaScript 與 native app 雙向溝通安全性的話可以參考 Android WebView 筆記

結語

我覺得很多 Android/iOS developer 在做 navigation 時沒有像那些 backend framework 處理 request 的思維。一般那些 backend framework 在處理 request/response 時都會經過一些 middleware,例如你可以定義一個 middleware 用來檢查用戶是否已登入,然後把它套在需要登入後才能進入的 route。這個 middleware 的大概內容是如果沒有登入就重定向到登入頁,然後直接 output response,不用再交去下一個 middleware 或者是該 route 對應的 controller。這其實跟 OkHttp 的 interceptor 相似。背後的 design pattern 是 Chain of Responsibility,是 Gang of Four Design Patterns 內的 pattern。

Google 將登入後才能進入否則重定向稱為 conditional navigation。它建議先讓用戶進入受限頁面,然後再用 Activity 層級開一個 UserViewModel,入面決定用戶是否已登入。而在受限頁面的 onViewCreated 就 observe UserViewModel 外露的是否已登入 LiveData。當發現未登入就馬上重定向到登入頁(準碓來說是在在受限頁面上顯示登入頁,沒有清除 back stack)。正因為沒有清除 back stack,用戶可以在登入頁按返回鍵回到之前的受限頁面。為了防止不斷重定向,它在受限頁面的 SavedStateHandle 加了一個 boolean flag 表明用戶是否成功登入,然後在受限頁面檢查那個 boolean flag 防止不斷在兩頁之間重定向。當然,今時今日不會有人這樣寫,因為兩個 ViewModel 之間通訊是很麻煩的事。通常都是用 dependency injection graph 加上一個能共用 instance 的 class 然後在入面放那些是否已登入的 Flow/LiveData/Observable。但 Google 這個提議最大問題是當頁面一多就很難 scale,因為它要求每頁都要加檢查的 code。

如果我們沿用 AndroidX Navigation component 又想做到類似 middleware 的效果,大概就是弄一個 suspending function 包起 navController.navigate,在那個 suspending function 內檢查要 navigate 的 route 有沒有 middleware 要執行,有的話就逐個執行。如果最終確認是可以直接進入目標頁面那就可以執行 navController.navigate。如果是要重定向的話就要為重定向頁做同樣的 middleware 執行動作,直到找到最終目的地為止。而用 suspending function 的原因是那些 middleware 可能需要做 I/O 動作,不像 navController.navigate 可以馬上完成。而 NavHost 外面要加一個載入畫面,當執行 middleware 時就蓋在 NavHost 上面不能讓用戶按到畫面其他地方。這樣就不用每一頁都要再檢查一次用戶是否已登入,因為在進入前已經被 middleware 檢查過。

很多 navigation library 偏向是補強 AndroidX Navigation component 欠缺生成 boilerplat code 的功能、或者令整個 app 變得更 MVI。反而中國出品的 navigation library 會做這類接近 middleware 的東西,但不知道有沒有考慮非同步的問題。它們甚至能做到由 backend 控制 navigation 的目的地(例如因應 A/B testing、feature flag 的值在 navigation 時開啟不同頁面)。

參考

comments powered by Disqus
+ PaperMod \ No newline at end of file diff --git a/404.html b/404.html index fa7fc794..7916236d 100644 --- a/404.html +++ b/404.html @@ -1,6 +1,6 @@ 404 Page not found | EricLog -
404
© 2024 EricLog +
404
+ PaperMod
\ No newline at end of file diff --git a/about/index.html b/about/index.html index ce77a18d..820814dc 100644 --- a/about/index.html +++ b/about/index.html @@ -1,6 +1,37 @@ About | EricLog -

About

我是 Eric Li,現職 Android app developer。

聯絡方法

comments powered by Disqus
© 2024 EricLog +

About

我是 Eric Li,現職 Android app developer。

聯絡方法

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/archives/index.html b/archives/index.html index 514eb196..45a4f0dd 100644 --- a/archives/index.html +++ b/archives/index.html @@ -1,6 +1,91 @@ Archives | EricLog -

2024  4

August  1

AndroidX Navigation component for Jetpack Compose type safety

August 18, 2024

June  1

Firebase Cloud Messaging legacy API

June 1, 2024

February  2

Android WebView 筆記

February 24, 2024

Android 14 migration

February 2, 2024

2023  2

July  1

Android pseudolocale

July 14, 2023

January  1

Android 13 Per-app Language Preferences

January 25, 2023

2022  4

July  3

Jetpack Compose Navigation component sub-graph

July 27, 2022

Jetpack Compose 遷移 (2)

July 26, 2022

Jetpack Compose 遷移 (1)

July 24, 2022

January  1

AndroidX Room Relational Query Method

January 29, 2022

2021  33

October  15

2021 iThome 鐵人賽 Day 30:Wrapping up

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

October 6, 2021

2021 iThome 鐵人賽 Day 20:Station list screen (2)

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

October 1, 2021

September  15

2021 iThome 鐵人賽 Day 15:Domain layer implementation

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

September 26, 2021

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

September 16, 2021

April  1

文字末端 icon 排版

April 10, 2021

March  2

Android SMS Verification APIs

March 14, 2021

Notification Channel 自訂音效

March 13, 2021

2020  7

November  1

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

November 14, 2020

August  2

Conventional Commits 和 commitlint

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

August 13, 2020

April  1

Kotlin Annotation Processor

April 27, 2020

March  2

Moshi Kotlin Codegen + R8 出現 parameter type is null

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

March 14, 2020

February  1

Firebase Cloud Messaging

February 29, 2020

2019  3

July  1

Android App Icon 規格

July 20, 2019

April  1

用 Google Apps Script 發送電郵

April 14, 2019

February  1

OpenRefine GREL 筆記

February 20, 2019

2018  4

October  1

用 Google Apps Script 建立 Google Calendar event

October 17, 2018

April  1

八達通新方向

April 16, 2018

March  1

Linkify 自動轉換成網址

March 10, 2018

February  1

Kotlin Parcelize

February 11, 2018

2017  10

December  1

React Native Android Multi-window 多視窗支援

December 1, 2017

October  1

Android Studio 3 的 Gradle 更新

October 27, 2017

September  1

Parcelable & Intent extra

September 16, 2017

June  3

TrainBoard for Android TV

June 26, 2017

SemVer

June 16, 2017

Kotlin for Android

June 12, 2017

May  1

Timber live template for Java/Kotlin

May 22, 2017

April  2

Android 隱藏 signing config

April 25, 2017

Spek

April 22, 2017

February  1

小米藍牙手柄

February 4, 2017

2016  9

December  2

小米盒子國際版開箱

December 19, 2016

nodetree 和 jazzy

December 7, 2016

October  1

Android Pay 登陸香港

October 21, 2016

September  2

自己造一個 CocoaPods Framework Pod

September 28, 2016

xUnique

September 24, 2016

August  1

在 Android app 內顯示 Git commit hash

August 29, 2016

July  2

Photosphere

July 31, 2016

Olympus E-M10

July 8, 2016

March  1

轉用 Hexo

March 21, 2016

2015  5

September  1

在 Windows 刪除路徑名太長的檔案

September 21, 2015

August  1

Parse + Android 收 Push Notification

August 30, 2015

May  1

整理 MP3 ID3 tag

May 17, 2015

March  1

Inline CSS

March 11, 2015

January  1

OnePlus One + Xperia Z2

January 12, 2015

2014  5

September  2

Data.One 的行車速度圖

September 25, 2014

ThinkPad 死電池

September 2, 2014

August  1

DevDocs

August 26, 2014

May  1

更新香港公眾假期日曆

May 3, 2014

March  1

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

March 23, 2014

2013  9

December  1

ConEmu

December 29, 2013

July  1

Xperia ion 維修記

July 10, 2013

June  3

萬用 Preprocessor Compiler:Prepros

June 8, 2013

Laraval 4 中文語系翻譯

June 6, 2013

Laravel 4

June 4, 2013

May  1

Bitbucket 免費 Academic License

May 27, 2013

March  1

SimCity

March 24, 2013

January  2

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

January 12, 2013

TrackPoint 使用滑鼠中鍵

January 11, 2013

2012  8

December  1

Facebook for Android 出了更新了!

December 14, 2012

September  2

Sony Xperia ion

September 8, 2012

Lenovo ThinkPad X230

September 3, 2012

August  1

八達通查閱易

August 4, 2012

May  1

散熱器新用法

May 27, 2012

April  1

2013 年公眾假期已刊憲

April 27, 2012

March  1

下載最新版本的 Chromium for Windows 方法

March 31, 2012

January  1

體驗全新公共圖書館系統

January 4, 2012

2011  6

December  1

已修正香港公眾假期 Google 日曆權限問題

December 5, 2011

August  1

八達通手機 App 風波

August 10, 2011

May  2

下載 Droid Sans 字型

May 11, 2011

2012 年公眾假期已刊憲

May 6, 2011

April  2

Moto Fail

April 22, 2011

資料一線通

April 11, 2011

2010  3

June  1

Photoshop 扮移軸

June 27, 2010

March  1

Google Map Street View 已經登陸香港

March 11, 2010

January  1

輕鐵指南資訊光碟

January 11, 2010

2009  5

November  1

Google Wave 試用報告

November 4, 2009

October  1

收到了 Google Wave Preview 的邀請函

October 24, 2009

May  1

Pencil Project

May 2, 2009

April  2

公共交通查詢服務 (PTES)

April 30, 2009

FontStruct:網上自製字型

April 8, 2009
© 2024 EricLog +

2024 + 4

August + 1

AndroidX Navigation component for Jetpack Compose type safety

August 18, 2024

June + 1

Firebase Cloud Messaging legacy API

June 1, 2024

February + 2

Android WebView 筆記

February 24, 2024

Android 14 migration

February 2, 2024

2023 + 2

July + 1

Android pseudolocale

July 14, 2023

January + 1

Android 13 Per-app Language Preferences

January 25, 2023

2022 + 4

July + 3

Jetpack Compose Navigation component sub-graph

July 27, 2022

Jetpack Compose 遷移 (2)

July 26, 2022

Jetpack Compose 遷移 (1)

July 24, 2022

January + 1

AndroidX Room Relational Query Method

January 29, 2022

2021 + 33

October + 15

2021 iThome 鐵人賽 Day 30:Wrapping up

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

October 6, 2021

2021 iThome 鐵人賽 Day 20:Station list screen (2)

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

October 1, 2021

September + 15

2021 iThome 鐵人賽 Day 15:Domain layer implementation

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

September 26, 2021

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

September 16, 2021

April + 1

文字末端 icon 排版

April 10, 2021

March + 2

Android SMS Verification APIs

March 14, 2021

Notification Channel 自訂音效

March 13, 2021

2020 + 7

November + 1

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

November 14, 2020

August + 2

Conventional Commits 和 commitlint

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

August 13, 2020

April + 1

Kotlin Annotation Processor

April 27, 2020

March + 2

Moshi Kotlin Codegen + R8 出現 parameter type is null

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

March 14, 2020

February + 1

Firebase Cloud Messaging

February 29, 2020

2019 + 3

July + 1

Android App Icon 規格

July 20, 2019

April + 1

用 Google Apps Script 發送電郵

April 14, 2019

February + 1

OpenRefine GREL 筆記

February 20, 2019

2018 + 4

October + 1

用 Google Apps Script 建立 Google Calendar event

October 17, 2018

April + 1

八達通新方向

April 16, 2018

March + 1

Linkify 自動轉換成網址

March 10, 2018

February + 1

Kotlin Parcelize

February 11, 2018

2017 + 10

December + 1

React Native Android Multi-window 多視窗支援

December 1, 2017

October + 1

Android Studio 3 的 Gradle 更新

October 27, 2017

September + 1

Parcelable & Intent extra

September 16, 2017

June + 3

TrainBoard for Android TV

June 26, 2017

SemVer

June 16, 2017

Kotlin for Android

June 12, 2017

May + 1

Timber live template for Java/Kotlin

May 22, 2017

April + 2

Android 隱藏 signing config

April 25, 2017

Spek

April 22, 2017

February + 1

小米藍牙手柄

February 4, 2017

2016 + 9

December + 2

小米盒子國際版開箱

December 19, 2016

nodetree 和 jazzy

December 7, 2016

October + 1

Android Pay 登陸香港

October 21, 2016

September + 2

自己造一個 CocoaPods Framework Pod

September 28, 2016

xUnique

September 24, 2016

August + 1

在 Android app 內顯示 Git commit hash

August 29, 2016

July + 2

Photosphere

July 31, 2016

Olympus E-M10

July 8, 2016

March + 1

轉用 Hexo

March 21, 2016

2015 + 5

September + 1

在 Windows 刪除路徑名太長的檔案

September 21, 2015

August + 1

Parse + Android 收 Push Notification

August 30, 2015

May + 1

整理 MP3 ID3 tag

May 17, 2015

March + 1

Inline CSS

March 11, 2015

January + 1

OnePlus One + Xperia Z2

January 12, 2015

2014 + 5

September + 2

Data.One 的行車速度圖

September 25, 2014

ThinkPad 死電池

September 2, 2014

August + 1

DevDocs

August 26, 2014

May + 1

更新香港公眾假期日曆

May 3, 2014

March + 1

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

March 23, 2014

2013 + 9

December + 1

ConEmu

December 29, 2013

July + 1

Xperia ion 維修記

July 10, 2013

June + 3

萬用 Preprocessor Compiler:Prepros

June 8, 2013

Laraval 4 中文語系翻譯

June 6, 2013

Laravel 4

June 4, 2013

May + 1

Bitbucket 免費 Academic License

May 27, 2013

March + 1

SimCity

March 24, 2013

January + 2

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

January 12, 2013

TrackPoint 使用滑鼠中鍵

January 11, 2013

2012 + 8

December + 1

Facebook for Android 出了更新了!

December 14, 2012

September + 2

Sony Xperia ion

September 8, 2012

Lenovo ThinkPad X230

September 3, 2012

August + 1

八達通查閱易

August 4, 2012

May + 1

散熱器新用法

May 27, 2012

April + 1

2013 年公眾假期已刊憲

April 27, 2012

March + 1

下載最新版本的 Chromium for Windows 方法

March 31, 2012

January + 1

體驗全新公共圖書館系統

January 4, 2012

2011 + 6

December + 1

已修正香港公眾假期 Google 日曆權限問題

December 5, 2011

August + 1

八達通手機 App 風波

August 10, 2011

May + 2

下載 Droid Sans 字型

May 11, 2011

2012 年公眾假期已刊憲

May 6, 2011

April + 2

Moto Fail

April 22, 2011

資料一線通

April 11, 2011

2010 + 3

June + 1

Photoshop 扮移軸

June 27, 2010

March + 1

Google Map Street View 已經登陸香港

March 11, 2010

January + 1

輕鐵指南資訊光碟

January 11, 2010

2009 + 5

November + 1

Google Wave 試用報告

November 4, 2009

October + 1

收到了 Google Wave Preview 的邀請函

October 24, 2009

May + 1

Pencil Project

May 2, 2009

April + 2

公共交通查詢服務 (PTES)

April 30, 2009

FontStruct:網上自製字型

April 8, 2009
+ PaperMod
\ No newline at end of file diff --git a/assets/css/stylesheet.014d92f7e42768013bc6e6c6aaf7562cad235ef48b65d729ba2e6b2ceb3a58e1.css b/assets/css/stylesheet.90d199bcefc47701be1700bdf90111f08c9ec8cafb7121eac1388d2f16c2cb69.css similarity index 50% rename from assets/css/stylesheet.014d92f7e42768013bc6e6c6aaf7562cad235ef48b65d729ba2e6b2ceb3a58e1.css rename to assets/css/stylesheet.90d199bcefc47701be1700bdf90111f08c9ec8cafb7121eac1388d2f16c2cb69.css index 88711772..57dc91e0 100644 --- a/assets/css/stylesheet.014d92f7e42768013bc6e6c6aaf7562cad235ef48b65d729ba2e6b2ceb3a58e1.css +++ b/assets/css/stylesheet.90d199bcefc47701be1700bdf90111f08c9ec8cafb7121eac1388d2f16c2cb69.css @@ -1,7 +1,7 @@ /* - PaperMod v7 + PaperMod v8+ License: MIT https://github.com/adityatelange/hugo-PaperMod/blob/master/LICENSE Copyright (c) 2020 nanxiaobei and adityatelange Copyright (c) 2021-2024 adityatelange */ -:root{--gap:24px;--content-gap:20px;--nav-width:1024px;--main-width:720px;--header-height:60px;--footer-height:60px;--radius:8px;--theme:rgb(255, 255, 255);--entry:rgb(255, 255, 255);--primary:rgb(30, 30, 30);--secondary:rgb(108, 108, 108);--tertiary:rgb(214, 214, 214);--content:rgb(31, 31, 31);--code-block-bg:rgb(28, 29, 33);--code-bg:rgb(245, 245, 245);--border:rgb(238, 238, 238)}.dark{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--code-bg)}.dark.list{background:var(--theme)}*,::after,::before{box-sizing:border-box}html{-webkit-tap-highlight-color:transparent;overflow-y:scroll;-webkit-text-size-adjust:100%;text-size-adjust:100%}a,button,body,h1,h2,h3,h4,h5,h6{color:var(--primary)}body{font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,Oxygen,Ubuntu,Cantarell,open sans,helvetica neue,sans-serif;font-size:18px;line-height:1.6;word-break:break-word;background:var(--theme)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section,table{display:block}h1,h2,h3,h4,h5,h6{line-height:1.2}h1,h2,h3,h4,h5,h6,p{margin-top:0;margin-bottom:0}ul{padding:0}a{text-decoration:none}body,figure,ul{margin:0}table{width:100%;border-collapse:collapse;border-spacing:0;overflow-x:auto;word-break:keep-all}button,input,textarea{padding:0;font:inherit;background:0 0;border:0}input,textarea{outline:0}button,input[type=button],input[type=submit]{cursor:pointer}input:-webkit-autofill,textarea:-webkit-autofill{box-shadow:0 0 0 50px var(--theme)inset}img{display:block;max-width:100%}.not-found{position:absolute;left:0;right:0;display:flex;align-items:center;justify-content:center;height:80%;font-size:160px;font-weight:700}.archive-posts{width:100%;font-size:16px}.archive-year{margin-top:40px}.archive-year:not(:last-of-type){border-bottom:2px solid var(--border)}.archive-month{display:flex;align-items:flex-start;padding:10px 0}.archive-month-header{margin:25px 0;width:200px}.archive-month:not(:last-of-type){border-bottom:1px solid var(--border)}.archive-entry{position:relative;padding:5px;margin:10px 0}.archive-entry-title{margin:5px 0;font-weight:400}.archive-count,.archive-meta{color:var(--secondary);font-size:14px}.footer,.top-link{font-size:12px;color:var(--secondary)}.footer{max-width:calc(var(--main-width) + var(--gap) * 2);margin:auto;padding:calc((var(--footer-height) - var(--gap))/2)var(--gap);text-align:center;line-height:24px}.footer span{margin-inline-start:1px;margin-inline-end:1px}.footer span:last-child{white-space:nowrap}.footer a{color:inherit;border-bottom:1px solid var(--secondary)}.footer a:hover{border-bottom:1px solid var(--primary)}.top-link{visibility:hidden;position:fixed;bottom:60px;right:30px;z-index:99;background:var(--tertiary);width:42px;height:42px;padding:12px;border-radius:64px;transition:visibility .5s,opacity .8s linear}.top-link,.top-link svg{filter:drop-shadow(0 0 0 var(--theme))}.footer a:hover,.top-link:hover{color:var(--primary)}.top-link:focus,#theme-toggle:focus{outline:0}.nav{display:flex;flex-wrap:wrap;justify-content:space-between;max-width:calc(var(--nav-width) + var(--gap) * 2);margin-inline-start:auto;margin-inline-end:auto;line-height:var(--header-height)}.nav a{display:block}.logo,#menu{display:flex;margin:auto var(--gap)}.logo{flex-wrap:inherit}.logo a{font-size:24px;font-weight:700}.logo a img,.logo a svg{display:inline;vertical-align:middle;pointer-events:none;transform:translate(0,-10%);border-radius:6px;margin-inline-end:8px}button#theme-toggle{font-size:26px;margin:auto 4px}body.dark #moon{vertical-align:middle;display:none}body:not(.dark) #sun{display:none}#menu{list-style:none;word-break:keep-all;overflow-x:auto;white-space:nowrap}#menu li+li{margin-inline-start:var(--gap)}#menu a{font-size:16px}#menu .active{font-weight:500;border-bottom:2px solid}.lang-switch li,.lang-switch ul,.logo-switches{display:inline-flex;margin:auto 4px}.lang-switch{display:flex;flex-wrap:inherit}.lang-switch a{margin:auto 3px;font-size:16px;font-weight:500}.logo-switches{flex-wrap:inherit}.main{position:relative;min-height:calc(100vh - var(--header-height) - var(--footer-height));max-width:calc(var(--main-width) + var(--gap) * 2);margin:auto;padding:var(--gap)}.page-header h1{font-size:40px}.pagination{display:flex}.pagination a{color:var(--theme);font-size:13px;line-height:36px;background:var(--primary);border-radius:calc(36px/2);padding:0 16px}.pagination .next{margin-inline-start:auto}.social-icons a{display:inline-flex;padding:10px}.social-icons a svg{height:26px;width:26px}code{direction:ltr}div.highlight,pre{position:relative}.copy-code{display:none;position:absolute;top:4px;right:4px;color:rgba(255,255,255,.8);background:rgba(78,78,78,.8);border-radius:var(--radius);padding:0 5px;font-size:14px;user-select:none}div.highlight:hover .copy-code,pre:hover .copy-code{display:block}.first-entry{position:relative;display:flex;flex-direction:column;justify-content:center;min-height:320px;margin:var(--gap)0 calc(var(--gap) * 2)}.first-entry .entry-header{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.first-entry .entry-header h1{font-size:34px;line-height:1.3}.first-entry .entry-content{margin:14px 0;font-size:16px;-webkit-line-clamp:3}.first-entry .entry-footer{font-size:14px}.home-info .entry-content{-webkit-line-clamp:unset}.post-entry{position:relative;margin-bottom:var(--gap);padding:var(--gap);background:var(--entry);border-radius:var(--radius);transition:transform .1s;border:1px solid var(--border)}.post-entry:active{transform:scale(.96)}.tag-entry .entry-cover{display:none}.entry-header h2{font-size:24px;line-height:1.3}.entry-content{margin:8px 0;color:var(--secondary);font-size:14px;line-height:1.6;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.entry-footer{color:var(--secondary);font-size:13px}.entry-link{position:absolute;left:0;right:0;top:0;bottom:0}.entry-hint{color:var(--secondary)}.entry-hint-parent{display:flex;justify-content:space-between}.entry-cover{font-size:14px;margin-bottom:var(--gap);text-align:center}.entry-cover img{border-radius:var(--radius);pointer-events:none;width:100%;height:auto}.entry-cover a{color:var(--secondary);box-shadow:0 1px 0 var(--primary)}.page-header,.post-header{margin:24px auto var(--content-gap)}.post-title{margin-bottom:2px;font-size:40px}.post-description{margin-top:10px;margin-bottom:5px}.post-meta,.breadcrumbs{color:var(--secondary);font-size:14px;display:flex;flex-wrap:wrap}.post-meta .i18n_list li{display:inline-flex;list-style:none;margin:auto 3px;box-shadow:0 1px 0 var(--secondary)}.breadcrumbs a{font-size:16px}.post-content{color:var(--content)}.post-content h3,.post-content h4,.post-content h5,.post-content h6{margin:24px 0 16px}.post-content h1{margin:40px auto 32px;font-size:40px}.post-content h2{margin:32px auto 24px;font-size:32px}.post-content h3{font-size:24px}.post-content h4{font-size:16px}.post-content h5{font-size:14px}.post-content h6{font-size:12px}.post-content a,.toc a:hover{box-shadow:0 1px;box-decoration-break:clone;-webkit-box-decoration-break:clone}.post-content a code{margin:auto 0;border-radius:0;box-shadow:0 -1px 0 var(--primary)inset}.post-content del{text-decoration:line-through}.post-content dl,.post-content ol,.post-content p,.post-content figure,.post-content ul{margin-bottom:var(--content-gap)}.post-content ol,.post-content ul{padding-inline-start:20px}.post-content li{margin-top:5px}.post-content li p{margin-bottom:0}.post-content dl{display:flex;flex-wrap:wrap;margin:0}.post-content dt{width:25%;font-weight:700}.post-content dd{width:75%;margin-inline-start:0;padding-inline-start:10px}.post-content dd~dd,.post-content dt~dt{margin-top:10px}.post-content table{margin-bottom:var(--content-gap)}.post-content table th,.post-content table:not(.highlighttable,.highlight table,.gist .highlight) td{min-width:80px;padding:8px 5px;line-height:1.5;border-bottom:1px solid var(--border)}.post-content table th{text-align:start}.post-content table:not(.highlighttable) td code:only-child{margin:auto 0}.post-content .highlight table{border-radius:var(--radius)}.post-content .highlight:not(table){margin:10px auto;background:var(--code-block-bg)!important;border-radius:var(--radius);direction:ltr}.post-content li>.highlight{margin-inline-end:0}.post-content ul pre{margin-inline-start:calc(var(--gap) * -2)}.post-content .highlight pre{margin:0}.post-content .highlighttable{table-layout:fixed}.post-content .highlighttable td:first-child{width:40px}.post-content .highlighttable td .linenodiv{padding-inline-end:0!important}.post-content .highlighttable td .highlight,.post-content .highlighttable td .linenodiv pre{margin-bottom:0}.post-content code{margin:auto 4px;padding:4px 6px;font-size:.78em;line-height:1.5;background:var(--code-bg);border-radius:2px}.post-content pre code{display:grid;margin:auto 0;padding:10px;color:#d5d5d6;background:var(--code-block-bg)!important;border-radius:var(--radius);overflow-x:auto;word-break:break-all}.post-content blockquote{margin:20px 0;padding:0 14px;border-inline-start:3px solid var(--primary)}.post-content hr{margin:30px 0;height:2px;background:var(--tertiary);border:0}.post-content iframe{max-width:100%}.post-content img{border-radius:4px;margin:1rem 0}.post-content img[src*="#center"]{margin:1rem auto}.post-content figure.align-center{text-align:center}.post-content figure>figcaption{color:var(--primary);font-size:16px;font-weight:700;margin:8px 0 16px}.post-content figure>figcaption>p{color:var(--secondary);font-size:14px;font-weight:400}.toc{margin:0 2px 40px;border:1px solid var(--border);background:var(--code-bg);border-radius:var(--radius);padding:.4em}.dark .toc{background:var(--entry)}.toc details summary{cursor:zoom-in;margin-inline-start:20px}.toc details[open] summary{cursor:zoom-out}.toc .details{display:inline;font-weight:500}.toc .inner{margin:0 20px;padding:10px 20px}.toc li ul{margin-inline-start:var(--gap)}.toc summary:focus{outline:0}.post-footer{margin-top:56px}.post-footer>*{margin-bottom:10px}.post-tags{display:flex;flex-wrap:wrap;gap:10px}.post-tags li{display:inline-block}.post-tags a,.share-buttons,.paginav{border-radius:var(--radius);background:var(--code-bg);border:1px solid var(--border)}.post-tags a{display:block;padding:0 14px;color:var(--secondary);font-size:14px;line-height:34px;background:var(--code-bg)}.post-tags a:hover,.paginav a:hover{background:var(--border)}.share-buttons{padding:10px;display:flex;justify-content:center;overflow-x:auto;gap:10px}.share-buttons li,.share-buttons a{display:inline-flex}.share-buttons a:not(:last-of-type){margin-inline-end:12px}h1:hover .anchor,h2:hover .anchor,h3:hover .anchor,h4:hover .anchor,h5:hover .anchor,h6:hover .anchor{display:inline-flex;color:var(--secondary);margin-inline-start:8px;font-weight:500;user-select:none}.paginav{display:flex;line-height:30px}.paginav a{padding-inline-start:14px;padding-inline-end:14px;border-radius:var(--radius)}.paginav .title{letter-spacing:1px;text-transform:uppercase;font-size:small;color:var(--secondary)}.paginav .prev,.paginav .next{width:50%}.paginav span:hover:not(.title){box-shadow:0 1px}.paginav .next{margin-inline-start:auto;text-align:right}[dir=rtl] .paginav .next{text-align:left}h1>a>svg{display:inline}img.in-text{display:inline;margin:auto}.buttons,.main .profile{display:flex;justify-content:center}.main .profile{align-items:center;min-height:calc(100vh - var(--header-height) - var(--footer-height) - (var(--gap) * 2));text-align:center}.profile .profile_inner{display:flex;flex-direction:column;align-items:center;gap:10px}.profile img{border-radius:50%}.buttons{flex-wrap:wrap;max-width:400px}.button{background:var(--tertiary);border-radius:var(--radius);margin:8px;padding:6px;transition:transform .1s}.button-inner{padding:0 8px}.button:active{transform:scale(.96)}#searchbox input{padding:4px 10px;width:100%;color:var(--primary);font-weight:700;border:2px solid var(--tertiary);border-radius:var(--radius)}#searchbox input:focus{border-color:var(--secondary)}#searchResults li{list-style:none;border-radius:var(--radius);padding:10px;margin:10px 0;position:relative;font-weight:500}#searchResults{margin:10px 0;width:100%}#searchResults li:active{transition:transform .1s;transform:scale(.98)}#searchResults a{position:absolute;width:100%;height:100%;top:0;left:0;outline:none}#searchResults .focus{transform:scale(.98);border:2px solid var(--tertiary)}.terms-tags li{display:inline-block;margin:10px;font-weight:500}.terms-tags a{display:block;padding:3px 10px;background:var(--tertiary);border-radius:6px;transition:transform .1s}.terms-tags a:active{background:var(--tertiary);transform:scale(.96)}.bg{color:#cad3f5;background-color:#24273a}.chroma{color:#cad3f5;background-color:#24273a}.chroma .x{}.chroma .err{color:#ed8796}.chroma .cl{}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{background-color:#474733}.chroma .lnt{white-space:pre;-webkit-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8087a2}.chroma .ln{white-space:pre;-webkit-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8087a2}.chroma .line{display:flex}.chroma .k{color:#c6a0f6}.chroma .kc{color:#f5a97f}.chroma .kd{color:#ed8796}.chroma .kn{color:#8bd5ca}.chroma .kp{color:#c6a0f6}.chroma .kr{color:#c6a0f6}.chroma .kt{color:#ed8796}.chroma .n{}.chroma .na{color:#8aadf4}.chroma .nb{color:#91d7e3}.chroma .bp{color:#91d7e3}.chroma .nc{color:#eed49f}.chroma .no{color:#eed49f}.chroma .nd{color:#8aadf4;font-weight:700}.chroma .ni{color:#8bd5ca}.chroma .ne{color:#f5a97f}.chroma .nf{color:#8aadf4}.chroma .fm{color:#8aadf4}.chroma .nl{color:#91d7e3}.chroma .nn{color:#f5a97f}.chroma .nx{}.chroma .py{color:#f5a97f}.chroma .nt{color:#c6a0f6}.chroma .nv{color:#f4dbd6}.chroma .vc{color:#f4dbd6}.chroma .vg{color:#f4dbd6}.chroma .vi{color:#f4dbd6}.chroma .vm{color:#f4dbd6}.chroma .l{}.chroma .ld{}.chroma .s{color:#a6da95}.chroma .sa{color:#ed8796}.chroma .sb{color:#a6da95}.chroma .sc{color:#a6da95}.chroma .dl{color:#8aadf4}.chroma .sd{color:#6e738d}.chroma .s2{color:#a6da95}.chroma .se{color:#8aadf4}.chroma .sh{color:#6e738d}.chroma .si{color:#a6da95}.chroma .sx{color:#a6da95}.chroma .sr{color:#8bd5ca}.chroma .s1{color:#a6da95}.chroma .ss{color:#a6da95}.chroma .m{color:#f5a97f}.chroma .mb{color:#f5a97f}.chroma .mf{color:#f5a97f}.chroma .mh{color:#f5a97f}.chroma .mi{color:#f5a97f}.chroma .il{color:#f5a97f}.chroma .mo{color:#f5a97f}.chroma .o{color:#91d7e3;font-weight:700}.chroma .ow{color:#91d7e3;font-weight:700}.chroma .p{}.chroma .c{color:#6e738d;font-style:italic}.chroma .ch{color:#6e738d;font-style:italic}.chroma .cm{color:#6e738d;font-style:italic}.chroma .c1{color:#6e738d;font-style:italic}.chroma .cs{color:#6e738d;font-style:italic}.chroma .cp{color:#6e738d;font-style:italic}.chroma .cpf{color:#6e738d;font-weight:700;font-style:italic}.chroma .g{}.chroma .gd{color:#ed8796;background-color:#363a4f}.chroma .ge{font-style:italic}.chroma .gr{color:#ed8796}.chroma .gh{color:#f5a97f;font-weight:700}.chroma .gi{color:#a6da95;background-color:#363a4f}.chroma .go{}.chroma .gp{}.chroma .gs{font-weight:700}.chroma .gu{color:#f5a97f;font-weight:700}.chroma .gt{color:#ed8796}.chroma .gl{text-decoration:underline}.chroma .w{}.chroma{background-color:unset!important}.chroma .hl{display:flex}.chroma .lnt{padding:0 0 0 12px}.highlight pre.chroma code{padding:8px 0}.highlight pre.chroma .line .cl,.chroma .ln{padding:0 10px}.chroma .lntd:last-of-type{width:100%}::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-track{background:var(--code-bg)}::-webkit-scrollbar-thumb{background:var(--tertiary);border:5px solid var(--theme);border-radius:var(--radius)}.list:not(.dark)::-webkit-scrollbar-thumb{border:5px solid var(--code-bg)}::-webkit-scrollbar-thumb:hover{background:var(--secondary)}::-webkit-scrollbar:not(.highlighttable,.highlight table,.gist .highlight){background:var(--theme)}.post-content .highlighttable td .highlight pre code::-webkit-scrollbar{display:none}.post-content :not(table) ::-webkit-scrollbar-thumb{border:2px solid var(--code-block-bg);background:#717175}.post-content :not(table) ::-webkit-scrollbar-thumb:hover{background:#a3a3a5}.gist table::-webkit-scrollbar-thumb{border:2px solid #fff;background:#adadad}.gist table::-webkit-scrollbar-thumb:hover{background:#707070}.post-content table::-webkit-scrollbar-thumb{border-width:2px}@media screen and (min-width:768px){::-webkit-scrollbar{width:19px;height:11px}}@media screen and (max-width:768px){:root{--gap:14px}.profile img{transform:scale(.85)}.first-entry{min-height:260px}.archive-month{flex-direction:column}.archive-year{margin-top:20px}.footer{padding:calc((var(--footer-height) - var(--gap) - 10px)/2)var(--gap)}}@media screen and (max-width:900px){.list .top-link{transform:translateY(-5rem)}}@media screen and (max-width:340px){.share-buttons{justify-content:unset}}@media(prefers-reduced-motion){.terms-tags a:active,.button:active,.post-entry:active,.top-link,#searchResults .focus,#searchResults li:active{transform:none}}body{font-family:inter,-apple-system,BlinkMacSystemFont,segoe ui,Helvetica,Arial,sans-serif,apple color emoji,segoe ui emoji}.post-content code{font-family:jetbrains mono,Menlo,Monaco,Courier,monospace;font-weight:300;font-variant-ligatures:normal}.chroma{color:#f8f8f2;background-color:#272822}.chroma .x{}.chroma .err{color:#960050;background-color:#1e0010}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f}.chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}.chroma .k{color:#66d9ef}.chroma .kc{color:#66d9ef}.chroma .kd{color:#66d9ef}.chroma .kn{color:#f92672}.chroma .kp{color:#66d9ef}.chroma .kr{color:#66d9ef}.chroma .kt{color:#66d9ef}.chroma .n{}.chroma .na{color:#a6e22e}.chroma .nb{}.chroma .bp{}.chroma .nc{color:#a6e22e}.chroma .no{color:#66d9ef}.chroma .nd{color:#a6e22e}.chroma .ni{}.chroma .ne{color:#a6e22e}.chroma .nf{color:#a6e22e}.chroma .fm{}.chroma .nl{}.chroma .nn{}.chroma .nx{color:#a6e22e}.chroma .py{}.chroma .nt{color:#f92672}.chroma .nv{}.chroma .vc{}.chroma .vg{}.chroma .vi{}.chroma .vm{}.chroma .l{color:#ae81ff}.chroma .ld{color:#e6db74}.chroma .s{color:#e6db74}.chroma .sa{color:#e6db74}.chroma .sb{color:#e6db74}.chroma .sc{color:#e6db74}.chroma .dl{color:#e6db74}.chroma .sd{color:#e6db74}.chroma .s2{color:#e6db74}.chroma .se{color:#ae81ff}.chroma .sh{color:#e6db74}.chroma .si{color:#e6db74}.chroma .sx{color:#e6db74}.chroma .sr{color:#e6db74}.chroma .s1{color:#e6db74}.chroma .ss{color:#e6db74}.chroma .m{color:#ae81ff}.chroma .mb{color:#ae81ff}.chroma .mf{color:#ae81ff}.chroma .mh{color:#ae81ff}.chroma .mi{color:#ae81ff}.chroma .il{color:#ae81ff}.chroma .mo{color:#ae81ff}.chroma .o{color:#f92672}.chroma .ow{color:#f92672}.chroma .p{}.chroma .c{color:#75715e}.chroma .ch{color:#75715e}.chroma .cm{color:#75715e}.chroma .c1{color:#75715e}.chroma .cs{color:#75715e}.chroma .cp{color:#75715e}.chroma .cpf{color:#75715e}.chroma .g{}.chroma .gd{color:#f92672}.chroma .ge{font-style:italic}.chroma .gr{}.chroma .gh{}.chroma .gi{color:#a6e22e}.chroma .go{}.chroma .gp{}.chroma .gs{font-weight:700}.chroma .gu{color:#75715e}.chroma .gt{}.chroma .gl{}.chroma .w{} \ No newline at end of file +:root{--gap:24px;--content-gap:20px;--nav-width:1024px;--main-width:720px;--header-height:60px;--footer-height:60px;--radius:8px;--theme:rgb(255, 255, 255);--entry:rgb(255, 255, 255);--primary:rgb(30, 30, 30);--secondary:rgb(108, 108, 108);--tertiary:rgb(214, 214, 214);--content:rgb(31, 31, 31);--code-block-bg:rgb(28, 29, 33);--code-bg:rgb(245, 245, 245);--border:rgb(238, 238, 238)}.dark{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--code-bg)}.dark.list{background:var(--theme)}*,::after,::before{box-sizing:border-box}html{-webkit-tap-highlight-color:transparent;overflow-y:scroll;-webkit-text-size-adjust:100%;text-size-adjust:100%}a,button,body,h1,h2,h3,h4,h5,h6{color:var(--primary)}body{font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,Oxygen,Ubuntu,Cantarell,open sans,helvetica neue,sans-serif;font-size:18px;line-height:1.6;word-break:break-word;background:var(--theme)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section,table{display:block}h1,h2,h3,h4,h5,h6{line-height:1.2}h1,h2,h3,h4,h5,h6,p{margin-top:0;margin-bottom:0}ul{padding:0}a{text-decoration:none}body,figure,ul{margin:0}table{width:100%;border-collapse:collapse;border-spacing:0;overflow-x:auto;word-break:keep-all}button,input,textarea{padding:0;font:inherit;background:0 0;border:0}input,textarea{outline:0}button,input[type=button],input[type=submit]{cursor:pointer}input:-webkit-autofill,textarea:-webkit-autofill{box-shadow:0 0 0 50px var(--theme)inset}img{display:block;max-width:100%}.not-found{position:absolute;left:0;right:0;display:flex;align-items:center;justify-content:center;height:80%;font-size:160px;font-weight:700}.archive-posts{width:100%;font-size:16px}.archive-year{margin-top:40px}.archive-year:not(:last-of-type){border-bottom:2px solid var(--border)}.archive-month{display:flex;align-items:flex-start;padding:10px 0}.archive-month-header{margin:25px 0;width:200px}.archive-month:not(:last-of-type){border-bottom:1px solid var(--border)}.archive-entry{position:relative;padding:5px;margin:10px 0}.archive-entry-title{margin:5px 0;font-weight:400}.archive-count,.archive-meta{color:var(--secondary);font-size:14px}.footer,.top-link{font-size:12px;color:var(--secondary)}.footer{max-width:calc(var(--main-width) + var(--gap) * 2);margin:auto;padding:calc((var(--footer-height) - var(--gap))/2)var(--gap);text-align:center;line-height:24px}.footer span{margin-inline-start:1px;margin-inline-end:1px}.footer span:last-child{white-space:nowrap}.footer a{color:inherit;border-bottom:1px solid var(--secondary)}.footer a:hover{border-bottom:1px solid var(--primary)}.top-link{visibility:hidden;position:fixed;bottom:60px;right:30px;z-index:99;background:var(--tertiary);width:42px;height:42px;padding:12px;border-radius:64px;transition:visibility .5s,opacity .8s linear}.top-link,.top-link svg{filter:drop-shadow(0 0 0 var(--theme))}.footer a:hover,.top-link:hover{color:var(--primary)}.top-link:focus,#theme-toggle:focus{outline:0}.nav{display:flex;flex-wrap:wrap;justify-content:space-between;max-width:calc(var(--nav-width) + var(--gap) * 2);margin-inline-start:auto;margin-inline-end:auto;line-height:var(--header-height)}.nav a{display:block}.logo,#menu{display:flex;margin:auto var(--gap)}.logo{flex-wrap:inherit}.logo a{font-size:24px;font-weight:700}.logo a img,.logo a svg{display:inline;vertical-align:middle;pointer-events:none;transform:translate(0,-10%);border-radius:6px;margin-inline-end:8px}button#theme-toggle{font-size:26px;margin:auto 4px}body.dark #moon{vertical-align:middle;display:none}body:not(.dark) #sun{display:none}#menu{list-style:none;word-break:keep-all;overflow-x:auto;white-space:nowrap}#menu li+li{margin-inline-start:var(--gap)}#menu a{font-size:16px}#menu .active{font-weight:500;border-bottom:2px solid}.lang-switch li,.lang-switch ul,.logo-switches{display:inline-flex;margin:auto 4px}.lang-switch{display:flex;flex-wrap:inherit}.lang-switch a{margin:auto 3px;font-size:16px;font-weight:500}.logo-switches{flex-wrap:inherit}.main{position:relative;min-height:calc(100vh - var(--header-height) - var(--footer-height));max-width:calc(var(--main-width) + var(--gap) * 2);margin:auto;padding:var(--gap)}.page-header h1{font-size:40px}.pagination{display:flex}.pagination a{color:var(--theme);font-size:13px;line-height:36px;background:var(--primary);border-radius:calc(36px/2);padding:0 16px}.pagination .next{margin-inline-start:auto}.social-icons a{display:inline-flex;padding:10px}.social-icons a svg{height:26px;width:26px}code{direction:ltr}div.highlight,pre{position:relative}.copy-code{display:none;position:absolute;top:4px;right:4px;color:rgba(255,255,255,.8);background:rgba(78,78,78,.8);border-radius:var(--radius);padding:0 5px;font-size:14px;user-select:none}div.highlight:hover .copy-code,pre:hover .copy-code{display:block}.first-entry{position:relative;display:flex;flex-direction:column;justify-content:center;min-height:320px;margin:var(--gap)0 calc(var(--gap) * 2)}.first-entry .entry-header{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.first-entry .entry-header h1{font-size:34px;line-height:1.3}.first-entry .entry-content{margin:14px 0;font-size:16px;-webkit-line-clamp:3}.first-entry .entry-footer{font-size:14px}.home-info .entry-content{-webkit-line-clamp:unset}.post-entry{position:relative;margin-bottom:var(--gap);padding:var(--gap);background:var(--entry);border-radius:var(--radius);transition:transform .1s;border:1px solid var(--border)}.post-entry:active{transform:scale(.96)}.tag-entry .entry-cover{display:none}.entry-header h2{font-size:24px;line-height:1.3}.entry-content{margin:8px 0;color:var(--secondary);font-size:14px;line-height:1.6;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.entry-footer{color:var(--secondary);font-size:13px}.entry-link{position:absolute;left:0;right:0;top:0;bottom:0}.entry-hint{color:var(--secondary)}.entry-hint-parent{display:flex;justify-content:space-between}.entry-cover{font-size:14px;margin-bottom:var(--gap);text-align:center}.entry-cover img{border-radius:var(--radius);pointer-events:none;width:100%;height:auto}.entry-cover a{color:var(--secondary);box-shadow:0 1px 0 var(--primary)}.page-header,.post-header{margin:24px auto var(--content-gap)}.post-title{margin-bottom:2px;font-size:40px}.post-description{margin-top:10px;margin-bottom:5px}.post-meta,.breadcrumbs{color:var(--secondary);font-size:14px;display:flex;flex-wrap:wrap}.post-meta .i18n_list li{display:inline-flex;list-style:none;margin:auto 3px;box-shadow:0 1px 0 var(--secondary)}.breadcrumbs a{font-size:16px}.post-content{color:var(--content)}.post-content h3,.post-content h4,.post-content h5,.post-content h6{margin:24px 0 16px}.post-content h1{margin:40px auto 32px;font-size:40px}.post-content h2{margin:32px auto 24px;font-size:32px}.post-content h3{font-size:24px}.post-content h4{font-size:16px}.post-content h5{font-size:14px}.post-content h6{font-size:12px}.post-content a,.toc a:hover{box-shadow:0 1px;box-decoration-break:clone;-webkit-box-decoration-break:clone}.post-content a code{margin:auto 0;border-radius:0;box-shadow:0 -1px 0 var(--primary)inset}.post-content del{text-decoration:line-through}.post-content dl,.post-content ol,.post-content p,.post-content figure,.post-content ul{margin-bottom:var(--content-gap)}.post-content ol,.post-content ul{padding-inline-start:20px}.post-content li{margin-top:5px}.post-content li p{margin-bottom:0}.post-content dl{display:flex;flex-wrap:wrap;margin:0}.post-content dt{width:25%;font-weight:700}.post-content dd{width:75%;margin-inline-start:0;padding-inline-start:10px}.post-content dd~dd,.post-content dt~dt{margin-top:10px}.post-content table{margin-bottom:var(--content-gap)}.post-content table th,.post-content table:not(.highlighttable,.highlight table,.gist .highlight) td{min-width:80px;padding:8px 5px;line-height:1.5;border-bottom:1px solid var(--border)}.post-content table th{text-align:start}.post-content table:not(.highlighttable) td code:only-child{margin:auto 0}.post-content .highlight table{border-radius:var(--radius)}.post-content .highlight:not(table){margin:10px auto;background:var(--code-block-bg)!important;border-radius:var(--radius);direction:ltr}.post-content li>.highlight{margin-inline-end:0}.post-content ul pre{margin-inline-start:calc(var(--gap) * -2)}.post-content .highlight pre{margin:0}.post-content .highlighttable{table-layout:fixed}.post-content .highlighttable td:first-child{width:40px}.post-content .highlighttable td .linenodiv{padding-inline-end:0!important}.post-content .highlighttable td .highlight,.post-content .highlighttable td .linenodiv pre{margin-bottom:0}.post-content code{margin:auto 4px;padding:4px 6px;font-size:.78em;line-height:1.5;background:var(--code-bg);border-radius:2px}.post-content pre code{display:grid;margin:auto 0;padding:10px;color:#d5d5d6;background:var(--code-block-bg)!important;border-radius:var(--radius);overflow-x:auto;word-break:break-all}.post-content blockquote{margin:20px 0;padding:0 14px;border-inline-start:3px solid var(--primary)}.post-content hr{margin:30px 0;height:2px;background:var(--tertiary);border:0}.post-content iframe{max-width:100%}.post-content img{border-radius:4px;margin:1rem 0}.post-content img[src*="#center"]{margin:1rem auto}.post-content figure.align-center{text-align:center}.post-content figure>figcaption{color:var(--primary);font-size:16px;font-weight:700;margin:8px 0 16px}.post-content figure>figcaption>p{color:var(--secondary);font-size:14px;font-weight:400}.toc{margin:0 2px 40px;border:1px solid var(--border);background:var(--code-bg);border-radius:var(--radius);padding:.4em}.dark .toc{background:var(--entry)}.toc details summary{cursor:zoom-in;margin-inline-start:10px;user-select:none}.toc details[open] summary{cursor:zoom-out}.toc .details{display:inline;font-weight:500}.toc .inner{margin:5px 20px 0;padding:0 10px;opacity:.9}.toc li ul{margin-inline-start:var(--gap)}.toc summary:focus{outline:0}.post-footer{margin-top:56px}.post-footer>*{margin-bottom:10px}.post-tags{display:flex;flex-wrap:wrap;gap:10px}.post-tags li{display:inline-block}.post-tags a,.share-buttons,.paginav{border-radius:var(--radius);background:var(--code-bg);border:1px solid var(--border)}.post-tags a{display:block;padding:0 14px;color:var(--secondary);font-size:14px;line-height:34px;background:var(--code-bg)}.post-tags a:hover,.paginav a:hover{background:var(--border)}.share-buttons{padding:10px;display:flex;justify-content:center;overflow-x:auto;gap:10px}.share-buttons li,.share-buttons a{display:inline-flex}.share-buttons a:not(:last-of-type){margin-inline-end:12px}h1:hover .anchor,h2:hover .anchor,h3:hover .anchor,h4:hover .anchor,h5:hover .anchor,h6:hover .anchor{display:inline-flex;color:var(--secondary);margin-inline-start:8px;font-weight:500;user-select:none}.paginav{display:flex;line-height:30px}.paginav a{padding-inline-start:14px;padding-inline-end:14px;border-radius:var(--radius)}.paginav .title{letter-spacing:1px;text-transform:uppercase;font-size:small;color:var(--secondary)}.paginav .prev,.paginav .next{width:50%}.paginav span:hover:not(.title){box-shadow:0 1px}.paginav .next{margin-inline-start:auto;text-align:right}[dir=rtl] .paginav .next{text-align:left}h1>a>svg{display:inline}img.in-text{display:inline;margin:auto}.buttons,.main .profile{display:flex;justify-content:center}.main .profile{align-items:center;min-height:calc(100vh - var(--header-height) - var(--footer-height) - (var(--gap) * 2));text-align:center}.profile .profile_inner{display:flex;flex-direction:column;align-items:center;gap:10px}.profile img{border-radius:50%}.buttons{flex-wrap:wrap;max-width:400px}.button{background:var(--tertiary);border-radius:var(--radius);margin:8px;padding:6px;transition:transform .1s}.button-inner{padding:0 8px}.button:active{transform:scale(.96)}#searchbox input{padding:4px 10px;width:100%;color:var(--primary);font-weight:700;border:2px solid var(--tertiary);border-radius:var(--radius)}#searchbox input:focus{border-color:var(--secondary)}#searchResults li{list-style:none;border-radius:var(--radius);padding:10px;margin:10px 0;position:relative;font-weight:500}#searchResults{margin:10px 0;width:100%}#searchResults li:active{transition:transform .1s;transform:scale(.98)}#searchResults a{position:absolute;width:100%;height:100%;top:0;left:0;outline:none}#searchResults .focus{transform:scale(.98);border:2px solid var(--tertiary)}.terms-tags li{display:inline-block;margin:10px;font-weight:500}.terms-tags a{display:block;padding:3px 10px;background:var(--tertiary);border-radius:6px;transition:transform .1s}.terms-tags a:active{background:var(--tertiary);transform:scale(.96)}.bg{color:#cad3f5;background-color:#24273a}.chroma{color:#cad3f5;background-color:#24273a}.chroma .x{}.chroma .err{color:#ed8796}.chroma .cl{}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{background-color:#474733}.chroma .lnt{white-space:pre;-webkit-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8087a2}.chroma .ln{white-space:pre;-webkit-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8087a2}.chroma .line{display:flex}.chroma .k{color:#c6a0f6}.chroma .kc{color:#f5a97f}.chroma .kd{color:#ed8796}.chroma .kn{color:#8bd5ca}.chroma .kp{color:#c6a0f6}.chroma .kr{color:#c6a0f6}.chroma .kt{color:#ed8796}.chroma .n{}.chroma .na{color:#8aadf4}.chroma .nb{color:#91d7e3}.chroma .bp{color:#91d7e3}.chroma .nc{color:#eed49f}.chroma .no{color:#eed49f}.chroma .nd{color:#8aadf4;font-weight:700}.chroma .ni{color:#8bd5ca}.chroma .ne{color:#f5a97f}.chroma .nf{color:#8aadf4}.chroma .fm{color:#8aadf4}.chroma .nl{color:#91d7e3}.chroma .nn{color:#f5a97f}.chroma .nx{}.chroma .py{color:#f5a97f}.chroma .nt{color:#c6a0f6}.chroma .nv{color:#f4dbd6}.chroma .vc{color:#f4dbd6}.chroma .vg{color:#f4dbd6}.chroma .vi{color:#f4dbd6}.chroma .vm{color:#f4dbd6}.chroma .l{}.chroma .ld{}.chroma .s{color:#a6da95}.chroma .sa{color:#ed8796}.chroma .sb{color:#a6da95}.chroma .sc{color:#a6da95}.chroma .dl{color:#8aadf4}.chroma .sd{color:#6e738d}.chroma .s2{color:#a6da95}.chroma .se{color:#8aadf4}.chroma .sh{color:#6e738d}.chroma .si{color:#a6da95}.chroma .sx{color:#a6da95}.chroma .sr{color:#8bd5ca}.chroma .s1{color:#a6da95}.chroma .ss{color:#a6da95}.chroma .m{color:#f5a97f}.chroma .mb{color:#f5a97f}.chroma .mf{color:#f5a97f}.chroma .mh{color:#f5a97f}.chroma .mi{color:#f5a97f}.chroma .il{color:#f5a97f}.chroma .mo{color:#f5a97f}.chroma .o{color:#91d7e3;font-weight:700}.chroma .ow{color:#91d7e3;font-weight:700}.chroma .p{}.chroma .c{color:#6e738d;font-style:italic}.chroma .ch{color:#6e738d;font-style:italic}.chroma .cm{color:#6e738d;font-style:italic}.chroma .c1{color:#6e738d;font-style:italic}.chroma .cs{color:#6e738d;font-style:italic}.chroma .cp{color:#6e738d;font-style:italic}.chroma .cpf{color:#6e738d;font-weight:700;font-style:italic}.chroma .g{}.chroma .gd{color:#ed8796;background-color:#363a4f}.chroma .ge{font-style:italic}.chroma .gr{color:#ed8796}.chroma .gh{color:#f5a97f;font-weight:700}.chroma .gi{color:#a6da95;background-color:#363a4f}.chroma .go{}.chroma .gp{}.chroma .gs{font-weight:700}.chroma .gu{color:#f5a97f;font-weight:700}.chroma .gt{color:#ed8796}.chroma .gl{text-decoration:underline}.chroma .w{}.chroma{background-color:unset!important}.chroma .hl{display:flex}.chroma .lnt{padding:0 0 0 12px}.highlight pre.chroma code{padding:8px 0}.highlight pre.chroma .line .cl,.chroma .ln{padding:0 10px}.chroma .lntd:last-of-type{width:100%}::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-track{background:var(--code-bg)}::-webkit-scrollbar-thumb{background:var(--tertiary);border:5px solid var(--theme);border-radius:var(--radius)}.list:not(.dark)::-webkit-scrollbar-thumb{border:5px solid var(--code-bg)}::-webkit-scrollbar-thumb:hover{background:var(--secondary)}::-webkit-scrollbar:not(.highlighttable,.highlight table,.gist .highlight){background:var(--theme)}.post-content .highlighttable td .highlight pre code::-webkit-scrollbar{display:none}.post-content :not(table) ::-webkit-scrollbar-thumb{border:2px solid var(--code-block-bg);background:#717175}.post-content :not(table) ::-webkit-scrollbar-thumb:hover{background:#a3a3a5}.gist table::-webkit-scrollbar-thumb{border:2px solid #fff;background:#adadad}.gist table::-webkit-scrollbar-thumb:hover{background:#707070}.post-content table::-webkit-scrollbar-thumb{border-width:2px}@media screen and (min-width:768px){::-webkit-scrollbar{width:19px;height:11px}}@media screen and (max-width:768px){:root{--gap:14px}.profile img{transform:scale(.85)}.first-entry{min-height:260px}.archive-month{flex-direction:column}.archive-year{margin-top:20px}.footer{padding:calc((var(--footer-height) - var(--gap) - 10px)/2)var(--gap)}}@media screen and (max-width:900px){.list .top-link{transform:translateY(-5rem)}}@media screen and (max-width:340px){.share-buttons{justify-content:unset}}@media(prefers-reduced-motion){.terms-tags a:active,.button:active,.post-entry:active,.top-link,#searchResults .focus,#searchResults li:active{transform:none}}body{font-family:inter,-apple-system,BlinkMacSystemFont,segoe ui,Helvetica,Arial,sans-serif,apple color emoji,segoe ui emoji}.post-content code{font-family:jetbrains mono,Menlo,Monaco,Courier,monospace;font-weight:300;font-variant-ligatures:normal}.chroma{color:#f8f8f2;background-color:#272822}.chroma .x{}.chroma .err{color:#960050;background-color:#1e0010}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em;color:#7f7f7f}.chroma .ln{margin-right:.4em;padding:0 .4em;color:#7f7f7f}.chroma .k{color:#66d9ef}.chroma .kc{color:#66d9ef}.chroma .kd{color:#66d9ef}.chroma .kn{color:#f92672}.chroma .kp{color:#66d9ef}.chroma .kr{color:#66d9ef}.chroma .kt{color:#66d9ef}.chroma .n{}.chroma .na{color:#a6e22e}.chroma .nb{}.chroma .bp{}.chroma .nc{color:#a6e22e}.chroma .no{color:#66d9ef}.chroma .nd{color:#a6e22e}.chroma .ni{}.chroma .ne{color:#a6e22e}.chroma .nf{color:#a6e22e}.chroma .fm{}.chroma .nl{}.chroma .nn{}.chroma .nx{color:#a6e22e}.chroma .py{}.chroma .nt{color:#f92672}.chroma .nv{}.chroma .vc{}.chroma .vg{}.chroma .vi{}.chroma .vm{}.chroma .l{color:#ae81ff}.chroma .ld{color:#e6db74}.chroma .s{color:#e6db74}.chroma .sa{color:#e6db74}.chroma .sb{color:#e6db74}.chroma .sc{color:#e6db74}.chroma .dl{color:#e6db74}.chroma .sd{color:#e6db74}.chroma .s2{color:#e6db74}.chroma .se{color:#ae81ff}.chroma .sh{color:#e6db74}.chroma .si{color:#e6db74}.chroma .sx{color:#e6db74}.chroma .sr{color:#e6db74}.chroma .s1{color:#e6db74}.chroma .ss{color:#e6db74}.chroma .m{color:#ae81ff}.chroma .mb{color:#ae81ff}.chroma .mf{color:#ae81ff}.chroma .mh{color:#ae81ff}.chroma .mi{color:#ae81ff}.chroma .il{color:#ae81ff}.chroma .mo{color:#ae81ff}.chroma .o{color:#f92672}.chroma .ow{color:#f92672}.chroma .p{}.chroma .c{color:#75715e}.chroma .ch{color:#75715e}.chroma .cm{color:#75715e}.chroma .c1{color:#75715e}.chroma .cs{color:#75715e}.chroma .cp{color:#75715e}.chroma .cpf{color:#75715e}.chroma .g{}.chroma .gd{color:#f92672}.chroma .ge{font-style:italic}.chroma .gr{}.chroma .gh{}.chroma .gi{color:#a6e22e}.chroma .go{}.chroma .gp{}.chroma .gs{font-weight:700}.chroma .gu{color:#75715e}.chroma .gt{}.chroma .gl{}.chroma .w{} \ No newline at end of file diff --git a/fonts/cn-license/index.html b/fonts/cn-license/index.html index 2302aa87..cf42b0a7 100644 --- a/fonts/cn-license/index.html +++ b/fonts/cn-license/index.html @@ -1,6 +1,91 @@ CN License A | EricLog -

CN License A

CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。

CN License A 應用示範

CN License A 字元示範

字元

分類字元
數字0 1 2 3 4 5 6 7 8 9
大楷A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
小楷a b c d e f g h i j k l m n o p q r s t u v w x y z
簡體中文 使
繁體中文
其他-(U+002D) (U+2022) (U+2027)  (U+3000)

下載

comments powered by Disqus
© 2024 EricLog +

CN License A

CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。

CN License A 應用示範

CN License A 字元示範

字元

分類字元
數字0 1 2 3 4 5 6 7 8 9
大楷A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
小楷a b c d e f g h i j k l m n o p q r s t u v w x y z
簡體中文 使
繁體中文
其他-(U+002D) (U+2022) (U+2027)  (U+3000)

下載

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/fonts/highway-signage/index.html b/fonts/highway-signage/index.html index e7dd4ea2..a48eeebf 100644 --- a/fonts/highway-signage/index.html +++ b/fonts/highway-signage/index.html @@ -1,6 +1,9 @@ Highway Signage | EricLog -

Highway Signage

Highway Signage 示範

Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Medium 是用於藍﹑綠、啡底白字的指示牌;而 Heavy 則用於白、黃底黑字的指示牌。

Highway Signage 採用了部分 OpenType 功能。例如隨意分數、連體字和大楷斜綫位置微調。

為方便不支援 OpenType 字型名的軟件使用 Highway Signage(大多數 Windows 所附送的軟件都不支援),字型會在這些軟件中以 Regular 代表 Highway Signage Medium 及以 Bold 代表 Highway Signage Heavy。但在其他支援的軟件中仍會以 Medium 及 Heavy 作識別。

支援的 OpenType 功能

OpenType 功能說明
aalt (Access All Alternates)顯示字元所有替代字
case (Case-Sensitive Forms)大寫模式(用作大楷時的斜綫位置微調)
onum (Oldstyle Figures)舊式數字(用作切換高速公路編號字元)
frac (Fractions)分數(用作自組無分數線的分數)
nmur (Numerators)分子(用作自組無分數線的分數)
dnom (Denominators)分母(用作自組無分數線的分數)
kern (Kerning)字距調整
liga (Standard Ligatures)標準連體字
salt (Stylistic Alternates)替代字(用作標示其他替代字元)

特別字元

Unicode說明
U+E000Quote single + hyphen 連體字(因應設計標準而設)
U+E001Space + hyphen + space 連體字(因應設計標準而設)
U+E002適用於大寫字的 slash(因應設計標準而設)
U+E003適用於大寫字的 backslash(因應設計標準而設)
U+E050Double grave(方便組字)
U+E051Inverted breve(方便組字)
U+2004空白字元(長 1.5 sw,因應設計標準而設)
U+2005空白字元(長 1.0 sw,因應設計標準而設)
U+2006空白字元(長 0.5 sw,因應設計標準而設)
U+2007空白字元(長 4.0 sw,因應設計標準而設)

Stroke Width (sw)

Stroke Width(簡寫 sw)是道路指示牌設計標準的長度單位,4 sw 相當於字體的 x-height(小寫字母 x 的高度)。

  • 預設的空白字元 (U+0020) 是 2.5 sw,如需使用其他長度請用 U+2004U+2007 字元。
  • Comma (U+002C) 後空白為 1.5 sw 已由字距調整 (kerning) 完成,輸入時先輸入逗號,然後空白即可。
  • Hyphen (U+002D) 之間的 1.5 sw 空白已由連體字 (ligature) 完成,輸入時留意軟件是否支援 OpenType 連體字。
  • Quote right (U+2019) 後留 0.5 sw 空白已由字距調整 (kerning) 完成。
  • 如果與高速公路編號於同一行顯示時,標準規定高速公路編號需要上升 0.5 sw。字型內的高速公路編號字元已經預先上升 0.5 sw。

下載

參考

comments powered by Disqus
© 2024 EricLog +

Highway Signage

Highway Signage 示範

Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Medium 是用於藍﹑綠、啡底白字的指示牌;而 Heavy 則用於白、黃底黑字的指示牌。

Highway Signage 採用了部分 OpenType 功能。例如隨意分數、連體字和大楷斜綫位置微調。

為方便不支援 OpenType 字型名的軟件使用 Highway Signage(大多數 Windows 所附送的軟件都不支援),字型會在這些軟件中以 Regular 代表 Highway Signage Medium 及以 Bold 代表 Highway Signage Heavy。但在其他支援的軟件中仍會以 Medium 及 Heavy 作識別。

支援的 OpenType 功能

OpenType 功能說明
aalt (Access All Alternates)顯示字元所有替代字
case (Case-Sensitive Forms)大寫模式(用作大楷時的斜綫位置微調)
onum (Oldstyle Figures)舊式數字(用作切換高速公路編號字元)
frac (Fractions)分數(用作自組無分數線的分數)
nmur (Numerators)分子(用作自組無分數線的分數)
dnom (Denominators)分母(用作自組無分數線的分數)
kern (Kerning)字距調整
liga (Standard Ligatures)標準連體字
salt (Stylistic Alternates)替代字(用作標示其他替代字元)

特別字元

Unicode說明
U+E000Quote single + hyphen 連體字(因應設計標準而設)
U+E001Space + hyphen + space 連體字(因應設計標準而設)
U+E002適用於大寫字的 slash(因應設計標準而設)
U+E003適用於大寫字的 backslash(因應設計標準而設)
U+E050Double grave(方便組字)
U+E051Inverted breve(方便組字)
U+2004空白字元(長 1.5 sw,因應設計標準而設)
U+2005空白字元(長 1.0 sw,因應設計標準而設)
U+2006空白字元(長 0.5 sw,因應設計標準而設)
U+2007空白字元(長 4.0 sw,因應設計標準而設)

Stroke Width (sw)

Stroke Width(簡寫 sw)是道路指示牌設計標準的長度單位,4 sw 相當於字體的 x-height(小寫字母 x 的高度)。

  • 預設的空白字元 (U+0020) 是 2.5 sw,如需使用其他長度請用 U+2004U+2007 字元。
  • Comma (U+002C) 後空白為 1.5 sw 已由字距調整 (kerning) 完成,輸入時先輸入逗號,然後空白即可。
  • Hyphen (U+002D) 之間的 1.5 sw 空白已由連體字 (ligature) 完成,輸入時留意軟件是否支援 OpenType 連體字。
  • Quote right (U+2019) 後留 0.5 sw 空白已由字距調整 (kerning) 完成。
  • 如果與高速公路編號於同一行顯示時,標準規定高速公路編號需要上升 0.5 sw。字型內的高速公路編號字元已經預先上升 0.5 sw。

下載

參考

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/fonts/index.html b/fonts/index.html index 2b7e3a7f..9433fe62 100644 --- a/fonts/index.html +++ b/fonts/index.html @@ -1,6 +1,8 @@ Fonts | EricLog -

CN License A

CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。 字元 分類 字元 數字 0 1 2 3 4 5 6 7 8 9 大楷 A B C...

November 13, 2017

Highway Signage

Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Me...

October 10, 2016
© 2024 EricLog +

CN License A

CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。 +字元 分類 字元 數字 0 1 2 3 4 5 6 7 8 9 大楷 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 小楷 a b c d e f g h i j k l m n o p q r s t u v w x y z 簡體中文 京 津 冀 晋 蒙 辽 吉 黑 沪 苏 浙 皖 闽 赣 鲁 豫 鄂 湘 粤 桂 琼 渝 川 贵 云 藏 陕 甘 青 宁 新 港 澳 警 使 领 学 繁體中文 晉 遼 滬 蘇 閩 贛 魯 粵 瓊 貴 雲 陝 寧 領 學 其他 -(U+002D) •(U+2022) ‧(U+2027) (U+3000) 下載 字型檔及字元表

November 13, 2017

Highway Signage

Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Medium 是用於藍﹑綠、啡底白字的指示牌;而 Heavy 則用於白、黃底黑字的指示牌。 +...

October 10, 2016
+ PaperMod
\ No newline at end of file diff --git a/fonts/index.xml b/fonts/index.xml index 624216dd..c6d310d5 100644 --- a/fonts/index.xml +++ b/fonts/index.xml @@ -1 +1,44 @@ -Fonts on EricLoghttps://eric.swiftzer.net/fonts/Recent content in Fonts on EricLogHugo -- gohugo.iozh-twMon, 13 Nov 2017 22:16:00 +0800CN License Ahttps://eric.swiftzer.net/fonts/cn-license/Mon, 13 Nov 2017 22:16:00 +0800https://eric.swiftzer.net/fonts/cn-license/CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。 字元 分類 字元 數字 0 1 2 3 4 5 6 7 8 9 大楷 A B CHighway Signagehttps://eric.swiftzer.net/fonts/highway-signage/Mon, 10 Oct 2016 23:03:14 +0800https://eric.swiftzer.net/fonts/highway-signage/Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Me \ No newline at end of file +Fonts on EricLoghttps://eric.swiftzer.net/fonts/Recent content in Fonts on EricLogHugo -- 0.139.2zh-twMon, 13 Nov 2017 22:16:00 +0800CN License Ahttps://eric.swiftzer.net/fonts/cn-license/Mon, 13 Nov 2017 22:16:00 +0800https://eric.swiftzer.net/fonts/cn-license/<p>CN License A 是參照 GA 36-2007《中華人民共和國機動車號牌》當中的 440 × 140 mm 車號牌所使用的字體而造。</p> +<!-- more --> +<p><img alt="CN License A 應用示範" loading="lazy" src="https://eric.swiftzer.net/fonts/cn-license/demo.png"></p> +<p><img alt="CN License A 字元示範" loading="lazy" src="https://eric.swiftzer.net/fonts/cn-license/glyphs.png"></p> +<h2 id="字元">字元</h2> +<table> +<thead> +<tr> +<th>分類</th> +<th>字元</th> +</tr> +</thead> +<tbody> +<tr> +<td>數字</td> +<td><code>0</code> <code>1</code> <code>2</code> <code>3</code> <code>4</code> <code>5</code> <code>6</code> <code>7</code> <code>8</code> <code>9</code></td> +</tr> +<tr> +<td>大楷</td> +<td><code>A</code> <code>B</code> <code>C</code> <code>D</code> <code>E</code> <code>F</code> <code>G</code> <code>H</code> <code>I</code> <code>J</code> <code>K</code> <code>L</code> <code>M</code> <code>N</code> <code>O</code> <code>P</code> <code>Q</code> <code>R</code> <code>S</code> <code>T</code> <code>U</code> <code>V</code> <code>W</code> <code>X</code> <code>Y</code> <code>Z</code></td> +</tr> +<tr> +<td>小楷</td> +<td><code>a</code> <code>b</code> <code>c</code> <code>d</code> <code>e</code> <code>f</code> <code>g</code> <code>h</code> <code>i</code> <code>j</code> <code>k</code> <code>l</code> <code>m</code> <code>n</code> <code>o</code> <code>p</code> <code>q</code> <code>r</code> <code>s</code> <code>t</code> <code>u</code> <code>v</code> <code>w</code> <code>x</code> <code>y</code> <code>z</code></td> +</tr> +<tr> +<td>簡體中文</td> +<td><code>京</code> <code>津</code> <code>冀</code> <code>晋</code> <code>蒙</code> <code>辽</code> <code>吉</code> <code>黑</code> <code>沪</code> <code>苏</code> <code>浙</code> <code>皖</code> <code>闽</code> <code>赣</code> <code>鲁</code> <code>豫</code> <code>鄂</code> <code>湘</code> <code>粤</code> <code>桂</code> <code>琼</code> <code>渝</code> <code>川</code> <code>贵</code> <code>云</code> <code>藏</code> <code>陕</code> <code>甘</code> <code>青</code> <code>宁</code> <code>新</code> <code>港</code> <code>澳</code> <code>警</code> <code>使</code> <code>领</code> <code>学</code></td> +</tr> +<tr> +<td>繁體中文</td> +<td><code>晉</code> <code>遼</code> <code>滬</code> <code>蘇</code> <code>閩</code> <code>贛</code> <code>魯</code> <code>粵</code> <code>瓊</code> <code>貴</code> <code>雲</code> <code>陝</code> <code>寧</code> <code>領</code> <code>學</code></td> +</tr> +<tr> +<td>其他</td> +<td><code>-</code>(U+002D) <code>•</code>(U+2022) <code>‧</code>(U+2027) <code> </code>(U+3000)</td> +</tr> +</tbody> +</table> +<h2 id="下載">下載</h2> +<ul> +<li><a href="https://eric.swiftzer.net/fonts/cn-license/CNLicenseA.zip">字型檔及字元表</a></li> +</ul>Highway Signagehttps://eric.swiftzer.net/fonts/highway-signage/Mon, 10 Oct 2016 23:03:14 +0800https://eric.swiftzer.net/fonts/highway-signage/<p><img alt="Highway Signage 示範" loading="lazy" src="https://eric.swiftzer.net/fonts/highway-signage/demo.png"></p> +<p>Highway Signage 概念乃源自英國道路指示牌所使用之字型,同時亦有加入用於高速公路編號的字符。Highway Signage 共有兩種粗幼度:Medium 及 Heavy。Medium 是用於藍﹑綠、啡底白字的指示牌;而 Heavy 則用於白、黃底黑字的指示牌。</p> \ No newline at end of file diff --git a/gear/index.html b/gear/index.html index 0e2e1d93..c1631a3e 100644 --- a/gear/index.html +++ b/gear/index.html @@ -1,6 +1,12 @@ 裝備 | EricLog -

裝備

現正持有

相機

Olympus OM-D E-M10

這是我第一部可換鏡頭相機,於 2015 年 6 月購入。本來是考慮 Sony α6000,但最後因為價錢和 M4/3 系統鏡頭數量所以最後買了 E-M10。這部相機機身是復古造型,非常好看。Live Composite 功能夠特別。但就只有三軸防震(Olympus 後來推出的機型都有五軸防震)、高 ISO 下有很多雜訊和 1080p 拍片畫質很差。用了七年左右還未壞,只是間中出現入錯拍錯 mode。即是 mode dial 在開機前選了 A mode 然後開機,相機會顯示當前是 S mode。重新開機或者把 mode dial 切換到 S mode 然後到 A mode 又會回復正常。

OM System OM-1

2023 年 2 月購入,2022 年末籌備旅行時已經構思換相機。之前去旅行時沒有帶腳架,但就因為晚間怕手震而要用最大光圈那支 17mm f/1.8 鏡頭來拍照,不過拍出來的照片都是有很多雜訊而且視角來來去去都是這幾個。之後就開始留意 M4/3 有甚麼新型號的機身,發現原來出了 OM-1,擁有 7 級防手震、IP53 防塵防水滴、4K60P 影片拍攝。高 ISO 下比 E-M10 效果好了不少,加上近年 AI 去 noise 技術非常強勁救了不少多 noise 相片,所以都繼續用 M4/3 系統。(雖然把同期 trade-in 升級鏡頭都計算在內的話其實可以直接換系統)3 月去完旅行後發覺沒有買錯,7 級防手震和 IP53 在旅行時的確很實用。就算在城市夜間拍照都可以不用三腳架,如果是 Auto ISO 相機可能會選了高一點的 ISO 值,但其實可以試試再手動調低一點再拍多一兩次就會獲得更高畫質的相片。

鏡頭

Olympus M.Zuiko Digital 17mm F1.8

連同 E-M10 kit 一齊購入。由於光圈比兩支 kit 鏡大加上 35mm 等效焦距是 34mm 很容易使用,所以它是我常用鏡頭排第二位。雖然號稱「Movie & Still Compatible」,但拍片如果用連續對焦的話相機的收音咪會錄到鏡頭對焦的噠噠聲,查過其他用家的講法是整個批次都會有這個問題。鏡身顏色是香檳色而不是銀色,這是因為 Olympus 初期推出的 M4/3 機身是香檳色。買了之後其實有點後悔應該要選黑色,因為 Olympus 之後出的機身都不是香檳色。另一個特色是它有對焦離合器,可以輕易切換手動對焦而且有對焦尺。

OM System M.Zuiko Digital ED 40-150mm F4.0 PRO

於 2022 年 12 月的 OM Day trade-in 購入以取代相同焦段的 kit 鏡。本來都沒打算 trade-in 這支鏡,但發現 trade-in 價實在是太便宜,價錢低到連在場的 OM System 職員都不太相信。所以聽完講座吃過午飯後就再次返回現場購買,在排隊付款時排我前面那一位也是拿遠攝 kit 鏡跟 OM System trade-in。畫質比 kit 鏡有感提升(耀光比 kit 鏡少),但因為賣了 kit 鏡所以不能直接拍照片左右對比。鏡頭大小跟一罐汽水差不多,非常適合旅行用。雖然光圈只有 f/4 但配合 OM-1 的 7 級防手震城市夜間 zoom 到最遠拍出來的照片都不會發現手震,非常強大。

OM System M.Zuiko Digital ED 12-40mm F2.8 PRO II

於 2023 年 2 月 trade-in 購入,取代 E-M10 kit 的電 zoom 餅鏡。這支鏡頭是原本換機計劃同時要升級的鏡頭。這支鏡頭是跟 OM-1、40-150mm F4.0 PRO 同時上市,現在變相集齊 OM-1 同期上市系列。聽聞早幾年有傳聞 Olympus 賣盤那時 12-40mm F2.8 PRO 第一代是賣得比現在第二代便宜。其實第二代只是換了鍍膜和加強防塵防滴等級,如果本身有第一代的話其實不用再買。畫質方面應該沒甚麼好投訴,以前去過 Olympus 的試鏡班都聽過職員介紹說買了這支鏡頭就不會再用那幾支 f/1.8 定焦鏡頭,因為畫質比較高又不用換鏡頭。(那時未有 f/1.2 系列定焦鏡和 12-100mm F4.0 天涯鏡)到了現代的話 OM System 職員就會推介你買 12-100mm F4.0 IS PRO。其實旅遊的話我都覺得 12-100mm 比較方便,因為不用經常換鏡頭,又可以有機身鏡頭同步防震。換了這支 12-40mm F2.8 PRO 後不久就去旅行,幸好有換了有防塵防滴的機身和鏡頭,因為頭幾天都下雨,其中一天要穿雨衣然後拿相機出來邊行邊拍照,如果沒有 trade-in 之前的鏡頭就只能收機不能拍照。

Voigtländer Nokton 25mm F0.95

於 2023 年 6 月二手購入。買了 OM-1 後有考慮過之後想要甚麼鏡頭,其中有考慮過標準鏡和人像鏡,因為之前用的定焦鏡都是偏向廣角,反而標準和遠攝定焦鏡就沒有用過,而且想感受一下淺景深的效果。但一直都是考慮,沒有特別想要它,直頭有朋友分享了二手網站有人放售這支鏡頭。它是第一代而非官方網頁介紹的第二代 (Type II),差別在於第一代是有段光圈;第二代加設了有段無段光圈切換環,可以切換成無段光圈方便拍攝影片。這支標準鏡的特色是光圈很大。雖然現在 OM System/Olympus 和 Panasonic 都有推出大光圈自動定焦鏡,加上近年有不少中國品牌都推出了特大光圈手動定焦鏡,但這支鏡是在很早期推出,當年推出時沒太多選擇,就算是手動對焦兼賣得貴都有人買。去到現在的話應該不會有太多人買。其實 f/0.95 只能在部分場景才能用,因為整個畫面都化開,而且色散太明顯,通常都要收一級光圈來用。但這支鏡用來拍人像我覺得不錯,而且光圈不用收太細都有明顯的星芒效果,反而散景又不是特別吸引(因為散景光點起角)。

Panasonic Leica DG Summilux 9mm/F1.7 ASPH. (H-X09)

於 2023 年 11 月購入用來取代老蛙那支 7.5mm 手動鏡頭。跟老蛙那支比主要是金屬鏡身 vs 塑膠鏡身、有沒有防水滴和是不是自動鏡頭。雖然超廣角鏡再加上 M4/3 應該不用經常手動調校對焦,但結果有時就忘了對焦。加上廠方沒有提供 lens profile 自動修正,要手動校正不太方便,所以最後還是換成 Panasonic 9mm。畫質方面其實又不算特別好,可能價格定位是用來簡單拍 vlog 和無人機用所以沒有金屬鏡身和畫質不算特別好(解析度和變形比老蛙差),但勝在夠輕便。


曾經持有

鏡頭

Olympus M.Zuiko Digital ED 14-42mm F3.5-5.6 EZ

E-M10 雙鏡 kit 之一,這支標準變焦鏡頭是我最常用的鏡頭。配合 E-M10 機身外形美觀又可以放入細小的相機袋。電動 zoom 夠靜,但它出名的是馬達很容易故障。(所幸未遇過故障)

這支鏡已於 2023 年 2 月 trade-in 換成相近焦段的 PRO 鏡(即是 OM System M.ZUIKO DIGITAL ED 12-40mm F2.8 PRO II)。

Olympus M.Zuiko Digital ED 40-150mm F4.0-5.6 R

E-M10 雙鏡 kit 之一。相比起 14-42mm kit 鏡和 17mm 定焦鏡,這支遠攝鏡用量相對較少,大約是 14-42mm kit 鏡的 56%。其中原因是 E-M10 只有三軸防震所以在暗的地方比較難用到(因為容易手震模糊加上高 ISO 又多雜訊)和比較少帶出街用。

這支鏡已於 2022 年 12 月 trade-in 換成相同焦段的 PRO 鏡(即是 OM SYSTEM M.ZUIKO DIGITAL ED 40-150mm F4.0 PRO)。

Laowa 7.5mm f/2 MFT

一直都想要一支超廣角鏡頭,所以在一推出時經團購買入(2017 年 6 月)。這支是老蛙首支 M4/3 鏡頭(那時老蛙還未加入 M4/3 聯盟),亦是我第一支手動鏡頭。但因為超廣角鏡頭景深不會淺,所以平常用只需要對焦至無限遠就可以,不用經常對焦。但就導致有時忘記對焦,在電腦看才發現沒有對好焦。這支鏡頭 35mm 等效焦距是 15mm,視角太廣我覺得有點難用。但鏡頭輕便即使拿了出去用得少也不會覺得有大問題。畫質方面,中央畫質很好,但有明顯暗角及色差,有時在戶外日光還會有明顯偏色(主要是偏黃和青綠)。

comments powered by Disqus
© 2024 EricLog +

裝備

現正持有

相機

Olympus OM-D E-M10

這是我第一部可換鏡頭相機,於 2015 年 6 月購入。本來是考慮 Sony α6000,但最後因為價錢和 M4/3 系統鏡頭數量所以最後買了 E-M10。這部相機機身是復古造型,非常好看。Live Composite 功能夠特別。但就只有三軸防震(Olympus 後來推出的機型都有五軸防震)、高 ISO 下有很多雜訊和 1080p 拍片畫質很差。用了七年左右還未壞,只是間中出現入錯拍錯 mode。即是 mode dial 在開機前選了 A mode 然後開機,相機會顯示當前是 S mode。重新開機或者把 mode dial 切換到 S mode 然後到 A mode 又會回復正常。

OM System OM-1

2023 年 2 月購入,2022 年末籌備旅行時已經構思換相機。之前去旅行時沒有帶腳架,但就因為晚間怕手震而要用最大光圈那支 17mm f/1.8 鏡頭來拍照,不過拍出來的照片都是有很多雜訊而且視角來來去去都是這幾個。之後就開始留意 M4/3 有甚麼新型號的機身,發現原來出了 OM-1,擁有 7 級防手震、IP53 防塵防水滴、4K60P 影片拍攝。高 ISO 下比 E-M10 效果好了不少,加上近年 AI 去 noise 技術非常強勁救了不少多 noise 相片,所以都繼續用 M4/3 系統。(雖然把同期 trade-in 升級鏡頭都計算在內的話其實可以直接換系統)3 月去完旅行後發覺沒有買錯,7 級防手震和 IP53 在旅行時的確很實用。就算在城市夜間拍照都可以不用三腳架,如果是 Auto ISO 相機可能會選了高一點的 ISO 值,但其實可以試試再手動調低一點再拍多一兩次就會獲得更高畫質的相片。

鏡頭

Olympus M.Zuiko Digital 17mm F1.8

連同 E-M10 kit 一齊購入。由於光圈比兩支 kit 鏡大加上 35mm 等效焦距是 34mm 很容易使用,所以它是我常用鏡頭排第二位。雖然號稱「Movie & Still Compatible」,但拍片如果用連續對焦的話相機的收音咪會錄到鏡頭對焦的噠噠聲,查過其他用家的講法是整個批次都會有這個問題。鏡身顏色是香檳色而不是銀色,這是因為 Olympus 初期推出的 M4/3 機身是香檳色。買了之後其實有點後悔應該要選黑色,因為 Olympus 之後出的機身都不是香檳色。另一個特色是它有對焦離合器,可以輕易切換手動對焦而且有對焦尺。

OM System M.Zuiko Digital ED 40-150mm F4.0 PRO

於 2022 年 12 月的 OM Day trade-in 購入以取代相同焦段的 kit 鏡。本來都沒打算 trade-in 這支鏡,但發現 trade-in 價實在是太便宜,價錢低到連在場的 OM System 職員都不太相信。所以聽完講座吃過午飯後就再次返回現場購買,在排隊付款時排我前面那一位也是拿遠攝 kit 鏡跟 OM System trade-in。畫質比 kit 鏡有感提升(耀光比 kit 鏡少),但因為賣了 kit 鏡所以不能直接拍照片左右對比。鏡頭大小跟一罐汽水差不多,非常適合旅行用。雖然光圈只有 f/4 但配合 OM-1 的 7 級防手震城市夜間 zoom 到最遠拍出來的照片都不會發現手震,非常強大。

OM System M.Zuiko Digital ED 12-40mm F2.8 PRO II

於 2023 年 2 月 trade-in 購入,取代 E-M10 kit 的電 zoom 餅鏡。這支鏡頭是原本換機計劃同時要升級的鏡頭。這支鏡頭是跟 OM-1、40-150mm F4.0 PRO 同時上市,現在變相集齊 OM-1 同期上市系列。聽聞早幾年有傳聞 Olympus 賣盤那時 12-40mm F2.8 PRO 第一代是賣得比現在第二代便宜。其實第二代只是換了鍍膜和加強防塵防滴等級,如果本身有第一代的話其實不用再買。畫質方面應該沒甚麼好投訴,以前去過 Olympus 的試鏡班都聽過職員介紹說買了這支鏡頭就不會再用那幾支 f/1.8 定焦鏡頭,因為畫質比較高又不用換鏡頭。(那時未有 f/1.2 系列定焦鏡和 12-100mm F4.0 天涯鏡)到了現代的話 OM System 職員就會推介你買 12-100mm F4.0 IS PRO。其實旅遊的話我都覺得 12-100mm 比較方便,因為不用經常換鏡頭,又可以有機身鏡頭同步防震。換了這支 12-40mm F2.8 PRO 後不久就去旅行,幸好有換了有防塵防滴的機身和鏡頭,因為頭幾天都下雨,其中一天要穿雨衣然後拿相機出來邊行邊拍照,如果沒有 trade-in 之前的鏡頭就只能收機不能拍照。

Voigtländer Nokton 25mm F0.95

於 2023 年 6 月二手購入。買了 OM-1 後有考慮過之後想要甚麼鏡頭,其中有考慮過標準鏡和人像鏡,因為之前用的定焦鏡都是偏向廣角,反而標準和遠攝定焦鏡就沒有用過,而且想感受一下淺景深的效果。但一直都是考慮,沒有特別想要它,直頭有朋友分享了二手網站有人放售這支鏡頭。它是第一代而非官方網頁介紹的第二代 (Type II),差別在於第一代是有段光圈;第二代加設了有段無段光圈切換環,可以切換成無段光圈方便拍攝影片。這支標準鏡的特色是光圈很大。雖然現在 OM System/Olympus 和 Panasonic 都有推出大光圈自動定焦鏡,加上近年有不少中國品牌都推出了特大光圈手動定焦鏡,但這支鏡是在很早期推出,當年推出時沒太多選擇,就算是手動對焦兼賣得貴都有人買。去到現在的話應該不會有太多人買。其實 f/0.95 只能在部分場景才能用,因為整個畫面都化開,而且色散太明顯,通常都要收一級光圈來用。但這支鏡用來拍人像我覺得不錯,而且光圈不用收太細都有明顯的星芒效果,反而散景又不是特別吸引(因為散景光點起角)。

Panasonic Leica DG Summilux 9mm/F1.7 ASPH. (H-X09)

於 2023 年 11 月購入用來取代老蛙那支 7.5mm 手動鏡頭。跟老蛙那支比主要是金屬鏡身 vs 塑膠鏡身、有沒有防水滴和是不是自動鏡頭。雖然超廣角鏡再加上 M4/3 應該不用經常手動調校對焦,但結果有時就忘了對焦。加上廠方沒有提供 lens profile 自動修正,要手動校正不太方便,所以最後還是換成 Panasonic 9mm。畫質方面其實又不算特別好,可能價格定位是用來簡單拍 vlog 和無人機用所以沒有金屬鏡身和畫質不算特別好(解析度和變形比老蛙差),但勝在夠輕便。


曾經持有

鏡頭

Olympus M.Zuiko Digital ED 14-42mm F3.5-5.6 EZ

E-M10 雙鏡 kit 之一,這支標準變焦鏡頭是我最常用的鏡頭。配合 E-M10 機身外形美觀又可以放入細小的相機袋。電動 zoom 夠靜,但它出名的是馬達很容易故障。(所幸未遇過故障)

這支鏡已於 2023 年 2 月 trade-in 換成相近焦段的 PRO 鏡(即是 OM System M.ZUIKO DIGITAL ED 12-40mm F2.8 PRO II)。

Olympus M.Zuiko Digital ED 40-150mm F4.0-5.6 R

E-M10 雙鏡 kit 之一。相比起 14-42mm kit 鏡和 17mm 定焦鏡,這支遠攝鏡用量相對較少,大約是 14-42mm kit 鏡的 56%。其中原因是 E-M10 只有三軸防震所以在暗的地方比較難用到(因為容易手震模糊加上高 ISO 又多雜訊)和比較少帶出街用。

這支鏡已於 2022 年 12 月 trade-in 換成相同焦段的 PRO 鏡(即是 OM SYSTEM M.ZUIKO DIGITAL ED 40-150mm F4.0 PRO)。

Laowa 7.5mm f/2 MFT

一直都想要一支超廣角鏡頭,所以在一推出時經團購買入(2017 年 6 月)。這支是老蛙首支 M4/3 鏡頭(那時老蛙還未加入 M4/3 聯盟),亦是我第一支手動鏡頭。但因為超廣角鏡頭景深不會淺,所以平常用只需要對焦至無限遠就可以,不用經常對焦。但就導致有時忘記對焦,在電腦看才發現沒有對好焦。這支鏡頭 35mm 等效焦距是 15mm,視角太廣我覺得有點難用。但鏡頭輕便即使拿了出去用得少也不會覺得有大問題。畫質方面,中央畫質很好,但有明顯暗角及色差,有時在戶外日光還會有明顯偏色(主要是偏黃和青綠)。

comments powered by Disqus
+ PaperMod
\ No newline at end of file diff --git a/index.html b/index.html index b0f290a7..1fad80fe 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,24 @@ -EricLog -

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像...

January 29, 2022
© 2024 EricLog +EricLog +

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 Fragment、打開 Fragment 時要什麼參數和各 Fragment 之間如何導覽的 XML 檔案)。如果加上 Safe Args Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 Fragment 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。 +...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API。 +...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 +AndroidX WebKit AndroidX 其實有 WebKit 的 artifact androidx.webkit:webkit,但不是把整個 browser 加到 app 入面(WebView 實際在用的 web browser 是由 Google Play Store 提供,並可以 developer options 切換),而是把部分較新的 WebView 功能加個檢查 function,如果目前的 WebView 支援那個功能的話就可以執行這部分的 code。 +...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 +Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission FOREGROUND_SERVICE_DATA_SYNC 標明 foreground service 的目的。 +...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。 +...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。 +...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 nested navigation graph 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。 +...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 +Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 Activity 或者 Fragment。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式: +...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 Fragment 內的 onCreateView 加入 setContent 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。 +...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 intermediate data class)。 +...

January 29, 2022
+ PaperMod
\ No newline at end of file diff --git a/index.xml b/index.xml index 004bb4f8..8b7d269b 100644 --- a/index.xml +++ b/index.xml @@ -1 +1,10 @@ -EricLoghttps://eric.swiftzer.net/Recent content on EricLogHugo -- gohugo.iozh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 AndroidAndroid pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本裝備https://eric.swiftzer.net/gear/Sat, 31 Dec 2022 00:00:00 +0800https://eric.swiftzer.net/gear/現正持有 相機 Olympus OM-D E-M10 這是我第一部可換鏡頭相機,於 2015 年 6 月購入。本來是考慮 Sony α6000,但最後因為價錢和 M4/3 系統鏡頭數量所以最後買了 E-M10。這Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。 \ No newline at end of file +EricLoghttps://eric.swiftzer.net/Recent content on EricLogHugo -- 0.139.2zh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/<p>AndroidX Navigation component 是 Google 推出的 <a href="https://www.youtube.com/watch?v=2k8x8V77CrU">single <code>Activity</code> app</a> navigation library。本身是用 <code>Fragment</code> 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 <code>Fragment</code>、打開 <code>Fragment</code> 時要什麼參數和各 <code>Fragment</code> 之間如何導覽的 XML 檔案)。如果加上 <a href="https://developer.android.com/guide/navigation/use-graph/safe-args">Safe Args</a> Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 <code>Fragment</code> 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。</p>Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/<p>如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 <a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send">HTTP v1 API</a>。</p>Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/<p>好幾年都沒有特別去用 Android 的 <code>WebView</code>,近期工作需要用到 <code>WebView</code>,所以特別去查一下並將資料放在這篇文章內方便日後翻查。</p> +<h2 id="androidx-webkit">AndroidX WebKit</h2> +<p>AndroidX 其實有 WebKit 的 artifact <a href="https://maven.google.com/web/index.html#androidx.webkit:webkit"><code>androidx.webkit:webkit</code></a>,但不是把整個 browser 加到 app 入面(<code>WebView</code> 實際在用的 web browser 是由 <a href="https://play.google.com/store/apps/details?id=com.google.android.webview">Google Play Store 提供</a>,並可以 developer options 切換),而是把部分較新的 <code>WebView</code> 功能加個檢查 function,如果目前的 <code>WebView</code> 支援那個功能的話就可以執行這部分的 code。</p>Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/<p>最近把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的 API level 升到 34 (<a href="https://developer.android.com/about/versions/14">Android 14</a>),中間發現了一些問題,在這裏記錄一下。</p> +<h2 id="foreground-service">Foreground service</h2> +<p>MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission <code>FOREGROUND_SERVICE_DATA_SYNC</code> 標明 foreground service 的目的。</p>Android pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/<p>在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。</p>Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/<p>最近抽點時間把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 參照 <a href="https://github.com/android/nowinandroid">Now in Android</a> 示範項目更新一下,例如改用 TOML 版的 <a href="https://docs.gradle.org/current/userguide/platforms.html">version catalog</a>(之前是用 Kotlin DSL)、轉用 <code>includeBuild</code> 加 convention plugin 取代之前把 plugin 放在 <code>buildSrc</code> 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。</p>裝備https://eric.swiftzer.net/gear/Sat, 31 Dec 2022 00:00:00 +0800https://eric.swiftzer.net/gear/<h2 id="現正持有">現正持有</h2> +<h3 id="相機">相機</h3> +<h4 id="olympus-om-d-e-m10httpsomsystemcomhkproductdslrem10indexhtml"><a href="https://omsystem.com.hk/product/dslr/em10/index.html">Olympus OM-D E-M10</a></h4> +<p>這是我第一部可換鏡頭相機,於 2015 年 6 月購入。本來是考慮 <a href="https://www.sony.com.hk/zh/electronics/interchangeable-lens-cameras/ilce-6000-body-kit">Sony α6000</a>,但最後因為價錢和 M4/3 系統鏡頭數量所以最後買了 E-M10。這部相機機身是復古造型,非常好看。<a href="https://www.youtube.com/watch?v=8pRhtJPcbrM">Live Composite 功能</a>夠特別。但就只有三軸防震(Olympus 後來推出的機型都有五軸防震)、高 ISO 下有很多雜訊和 1080p 拍片畫質很差。用了七年左右還未壞,只是間中出現入錯拍錯 mode。即是 mode dial 在開機前選了 A mode 然後開機,相機會顯示當前是 S mode。重新開機或者把 mode dial 切換到 S mode 然後到 A mode 又會回復正常。</p>Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/<p>這次<a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">遷移到 Compose</a> 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 <a href="https://developer.android.com/guide/navigation/navigation-nested-graphs">nested navigation graph</a> 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。</p>Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/<p><a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">上一篇</a>提過如何將 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。</p> +<h2 id="dependency-injection">Dependency injection</h2> +<p>按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 <code>Activity</code> 或者 <code>Fragment</code>。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式:</p>Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/<p>近幾個月斷斷續續替 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的界面由傳統 view system(即是 layout XML)轉為 <a href="https://developer.android.com/jetpack/compose">Jetpack Compose</a>,順帶補上去年參加 <a href="https://eric.swiftzer.net/2021-ithome-ironman/">iThome 鐵人賽</a>時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 <code>Fragment</code> 內的 <code>onCreateView</code> 加入 <code>setContent</code> 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。</p> \ No newline at end of file diff --git a/page/10/index.html b/page/10/index.html index c6b62f60..d70a12bb 100644 --- a/page/10/index.html +++ b/page/10/index.html @@ -1,12 +1,19 @@ -EricLog -

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 -...

June 4, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話...

May 27, 2013

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 +EricLog +

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 +...

June 4, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話可以考慮用 Bitbucket。Bitbucket 最多可以與五個人共用私人的 repo,但它有推薦制度來增加共用名額,最多能與八個人共用同一個私人 repo。 +...

May 27, 2013

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 ...

March 24, 2013

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。 -...

January 12, 2013

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraN...

January 11, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

January 12, 2013

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。 +...

January 11, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。 +...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 ...

September 8, 2012

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 -...

September 3, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯...

August 4, 2012

散熱器新用法

舊電腦有個 Intel Pentium 4 原裝散熱器還未用過,非常新淨。拉絲鋁的材質看起來很前衛,拿來當信座都不錯。 信座正面 做法非常簡單: 將散熱器上的風扇拆走 在底部貼...

May 27, 2012
September 3, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。 +...

August 4, 2012

散熱器新用法

舊電腦有個 Intel Pentium 4 原裝散熱器還未用過,非常新淨。拉絲鋁的材質看起來很前衛,拿來當信座都不錯。 +信座正面 做法非常簡單: +將散熱器上的風扇拆走 在底部貼上雙面膠紙 貼上軟膠墊來防止散熱器刮花桌面 將信件插入散熱片之間的空隙即可 軟膠墊我是在先前買散裝 DVD 燒碟機的保護膠套中裁出來的。其實還可以用沙紙將邊角位磨到圓滑避免刮手。 +...

May 27, 2012
+ PaperMod \ No newline at end of file diff --git a/page/11/index.html b/page/11/index.html index efdeacbc..4c798529 100644 --- a/page/11/index.html +++ b/page/11/index.html @@ -1,7 +1,19 @@ -EricLog -

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是...

April 27, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe...

March 31, 2012

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋...

January 4, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已...

December 5, 2011

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊...

May 6, 2011

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯...

April 22, 2011

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線...

April 11, 2011

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111...

June 27, 2010
© 2024 EricLog +EricLog +

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是下載 iCal 檔的話就需要再下載並匯入香港公眾假期日曆過。 +...

April 27, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。 +...

March 31, 2012

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 AddThis 按鈕,實在令人難以相信這個是政府做的網站(就算是 gov.hk 都無這個做得出色)。 +...

January 4, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已經設定過權限,如果大家再發現日曆有問題的話請馬上通知,謝謝大家的支持! +...

December 5, 2011

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。 +...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊於 10 月 1 日,所以 10 月 1 日及 2 日都會是公眾假期。 +...

May 6, 2011

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 Android 2.2 更新會收不到 Google Chrome to Phone 的 Push notification 和 Moto 輸入法的問題。 +...

April 22, 2011

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。 +資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。 +...

April 11, 2011

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 +相片中白色的輕鐵列車就是新來港的 1117。 +Photoshop 扮移軸

June 27, 2010
+ PaperMod
\ No newline at end of file diff --git a/page/12/index.html b/page/12/index.html index a793d184..43fe68c4 100644 --- a/page/12/index.html +++ b/page/12/index.html @@ -1,10 +1,20 @@ -EricLog -

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環...

March 11, 2010

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一...

January 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 由於目前有太少人可以試用 Google W...

October 24, 2009

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖...

May 2, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: +EricLog +

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 +Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環、氹仔及澳門半島南部。拍攝出來的全景圖基本上都沒有大問題,只是有部分的模糊處理有錯。例如將巴士站牌、車身的非車牌部分都被模糊。較為有趣的相信是下圖的例子(位於天城路): +...

March 11, 2010

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一個路線圖程式。 +...

January 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 +Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave。Blip 就是一個個的訊息,亦即是在 Google Wave 對話的基本單位。一個 Wave 之中有不同的 Blip,其他成員可以就個別的 Blip 發表回應(訧像討論區的引用功能般)。那麼上一層的 Blip 就叫做 Wavelet。 +...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 +由於目前有太少人可以試用 Google Wave,所以暫時未能提供試用心得,日後將會補上。 +...

October 24, 2009

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。 +...

May 2, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: Microsoft OLE DB Provider for SQL Server error ‘80040e31’ Timeout expired /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -...

April 30, 2009

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 在 FontStruct 繪製字元「F」 FontStruct 是一個提供網...

April 8, 2009
April 30, 2009

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 +在 FontStruct 繪製字元「F」 FontStruct 是一個提供網上自製字型的服務。只需要在 FontStruct 免費注冊一個戶口,你就可以使用它以 Adobe Flex 製作的介面來繪製字元。別以為它是很難用,其實用法好像在使用小畫家般。只需要選好預製的形狀,然後在格網上畫上去便行了,網站首頁的影片亦有展示它的使用方法。 +...

April 8, 2009
+ PaperMod \ No newline at end of file diff --git a/page/2/index.html b/page/2/index.html index e6fae8c1..bd77a873 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -1,7 +1,40 @@ -EricLog -

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 文章目錄 終於來到最後一篇了!不經不覺已經寫了三十篇文章...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 文章目錄 我們終於來到第廿九篇,我們這次討論的題目都是之...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 文章目錄 終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最後一個功能:錯誤 banner。...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 文章目錄 我們這次會為班次頁加上自動更新和順帶為下一篇實...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 文章目錄 SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 文章目錄 由於我們在上一篇已經完成了成功載入班次的部分,...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最重要的頁面:抵站時間頁。這個頁...

October 6, 2021
© 2024 EricLog +EricLog +

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。 +...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 +文章目錄 +我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。 +...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。 +之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public: +...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。 +EtaPresenter 首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是: +...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockK 和 Strikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric。 +...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。 +...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 +文章目錄 +我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。 +我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值: +...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 +文章目錄 +SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。 +...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 +文章目錄 +由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。 +...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面: +...

October 6, 2021
+ PaperMod
\ No newline at end of file diff --git a/page/3/index.html b/page/3/index.html index fa98a746..293569e6 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -1,7 +1,40 @@ -EricLog -

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了 StationListAdap...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 文章目錄 最近兩篇都是講 navigation component,入面為了示...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 文章目錄 在 Android,navigation graph 是 resource 的...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 文章目錄 經過了兩個多星期後,我們終於開始進入 presentation layer 的部分...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 文章目錄 今天會為上一篇所寫的兩個 use case 加上 unit test。 GetLinesAndStationsUseCaseImplTest...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 文章目錄 經過這麼多集的 data layer 後,我們來到 domain layer。D...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 文章目錄 在繼續實作 domain layer 之前,我們會介紹一個方便日常開發...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 文章目錄 上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 文章目錄 今天會繼續寫 EtaResponseMapper...

September 26, 2021
© 2024 EricLog +EricLog +

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。 +...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 +文章目錄 +最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品: +...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml。 +eta.xml 在 project 的位置 先附上完整的內容,然後再慢慢講解入面的意思。 +...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 +文章目錄 +經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 Activity、Fragment、ViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。 +...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 +文章目錄 +今天會為上一篇所寫的兩個 use case 加上 unit test。 +GetLinesAndStationsUseCaseImplTest 這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepository 的 getLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。 +...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 +文章目錄 +經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 Activity、Fragment、ViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。 +...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 +文章目錄 +在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper。 +Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。 +...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。 +...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。 +...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 +文章目錄 +今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response: +...

September 26, 2021
+ PaperMod
\ No newline at end of file diff --git a/page/4/index.html b/page/4/index.html index 64983ed1..e4ddede8 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -1,7 +1,40 @@ -EricLog -

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 文章目錄 在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 文章目錄 上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Ins...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 文章目錄 上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 文章目錄 在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 文章目錄 在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 文章目錄 談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI l...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 文章目錄 JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 文章目錄 我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 文章目錄 Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 文章目錄 早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車...

September 16, 2021
© 2024 EricLog +EricLog +

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 +文章目錄 +在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(Strikt 和 MockK): +...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Instant、LocalDateTime 和 ZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。 +...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface: +interface Mapper<T, R> { suspend fun map(o: T): R } 有些 Android Clean Architecture 會每個 layer 都準備一個對應的 mapper interface,這次示範我們就簡化這一部分,全部 layer 都共用同一個 mapper interface,不論是由高層次 layer 去低層次 layer 還是由低層次 layer 去高層次 layer 都一樣。因為這個 mapper 都是為了在寫 unit test 時可以 mock 那個 interface 而不是 mock 那個 concrete implementation,所以它是不是共用 interface 問題不大。 +...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 +文章目錄 +在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。 +跨 layer 共用部分 不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。 +...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor。 +...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 +文章目錄 +談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。 +...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 +文章目錄 +JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,Gson、Moshi 和 Kotlin serialization 都是熱門的選擇。 +...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 +文章目錄 +我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。 +...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 +文章目錄 +Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture: +...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 +文章目錄 +早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。 +App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。 +...

September 16, 2021
+ PaperMod
\ No newline at end of file diff --git a/page/5/index.html b/page/5/index.html index 5d95eaff..05b28e3d 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -1,7 +1,19 @@ -EricLog -

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取...

November 14, 2020

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話...

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 in...

August 13, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在...

April 27, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自...

February 29, 2020
© 2024 EricLog +EricLog +

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而那個 Button 就固定在右邊。TextView 有 compound drawable,但如果想那個 icon 有 click listener 又用不了。所以最後都要分開 TextView 和 ImageView/ImageButton 兩個 view。 +...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後再把內文的驗證碼輸入到 app 中。如果想省卻用戶輪入文字的話有一些 app 會透過 READ_SMS 權限讀取 SMS 內容來抽取驗證碼,但 Google Play 已經限制非預設短訊 app 不可以有 READ_SMS 權限。 +...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各式 notification 的提示方法,例如有沒有音效、會不會彈出 heads-up notification 之類。如果 app 想自訂 notification 的聲音亦都要經 notification channel 設定(但用戶可以之後自行變更 notification channel 的音效) +...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法: +...

November 14, 2020

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。 +...

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 int、float、double 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。 +...

August 13, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。 +...

April 27, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。 +...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: +java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch Crashlytics Org Id > Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811 我的 app 是在 Firebase 開了兩個 project,一個是開發時用的,另一個是 production 用的。但只有 production 才有這個錯誤。如果把 release build 設為不上載 ProGuard/R8 mapping file 的話就不會有這個錯誤。 +...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 +如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。 +...

February 29, 2020
+ PaperMod
\ No newline at end of file diff --git a/page/6/index.html b/page/6/index.html index 5f865066..bb4b7eca 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -1,12 +1,22 @@ -EricLog -

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散...

July 20, 2019

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 準備內容 上次我們用 2019 年香港公眾假期作例...

April 14, 2019

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 +EricLog +

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon: +...

July 20, 2019

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 +準備內容 上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。 +...

April 14, 2019

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 Text facet 功能可以批量修改相近的資料,而不用寫程式 OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。 值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。 -...

February 20, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script...

October 17, 2018

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 +...

February 20, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。 +...

October 17, 2018

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。 -...

April 16, 2018

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular...

March 10, 2018

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boile...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserial...

September 16, 2017
April 16, 2018

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular expression 做檢查的話那句 regular expression 就會好長,而且要定時補上日後新推出的 TLD。 +...

March 10, 2018

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。 +@Parcelize data class Product(val name: String, val price: Double) : Parcelable 留意要在 build.gradle 加上: +...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。 +...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。 +...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable。 +...

September 16, 2017
+ PaperMod \ No newline at end of file diff --git a/page/7/index.html b/page/7/index.html index a51f8fc1..80ddb56e 100644 --- a/page/7/index.html +++ b/page/7/index.html @@ -1,12 +1,20 @@ -EricLog -

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。Trai...

June 26, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、min...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門...

April 25, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 +EricLog +

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。TrainBoard 提供港鐵將軍澳綫、東涌綫、機場快綫、迪士尼綫及西鐵綫列車的預計到達時間,而這個 app 是為 MTR Service Update 而做的。 +...

June 26, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 equals 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。 +...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。 +...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。 +...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門的方式來設定 release keystore 和密碼。而在 VCS checkout source code 後在 file system 補上 keystore.properties 和 keystore 未必可以在 CI 環境上做到。所以我將那個教學稍作改動,令到當 keystore.properties 不存在時就不提供 signing config,使它能 build 未加簽的 apk,然後才讓 CI 加簽 apk。 +...

April 25, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。 -...

April 22, 2017

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 -小米盒子國際版 ...

December 19, 2016

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖...

December 7, 2016

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。 +...

April 22, 2017

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。 +...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 +小米盒子國際版 ...

December 19, 2016

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 +nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 nodetree 指令就能印出目前目錄的結構。 +...

December 7, 2016

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。 除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。 ...

October 21, 2016
+ PaperMod \ No newline at end of file diff --git a/page/8/index.html b/page/8/index.html index 61633846..e1abc098 100644 --- a/page/8/index.html +++ b/page/8/index.html @@ -1,12 +1,21 @@ -EricLog -

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 -...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 -...

August 29, 2016

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 -...

July 8, 2016

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 Hexo 是一個用 Node 寫的 static site generator...

March 21, 2016

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是...

September 21, 2015

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 -...

August 30, 2015

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2...

May 17, 2015

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: +EricLog +

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 +...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。 +...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 +...

August 29, 2016

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 +以下是所需工具: +有全手動模式的相機 魚眼鏡 / 廣角鏡 三腳架 Hugin Photoshop 能修改檔案 XMP 的工具 器材 按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。 +...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 +...

July 8, 2016

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 +Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code> 之類的 tag,用了不少時間才寫完。 +...

March 21, 2016

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。 +...

September 21, 2015

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 +...

August 30, 2015

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 +如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2,而下面就只揀選 ID3v2.3 UTF-16。而在 Remove 部分則剔選 ID3v1 及 APE。這樣就應該不會再有亂碼。 +...

May 17, 2015

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: CSS 要 inline,<style> 並不是全部 email client 都能支援 排版要用 <table> 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 ...

March 11, 2015
+ PaperMod \ No newline at end of file diff --git a/page/9/index.html b/page/9/index.html index 461538a9..75eb791e 100644 --- a/page/9/index.html +++ b/page/9/index.html @@ -1,13 +1,19 @@ -EricLog -

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 +EricLog +

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 -...

January 12, 2015

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤...

September 25, 2014

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,...

September 2, 2014

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是...

August 26, 2014

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$ 和 $MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。 +...

January 12, 2015

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。 +...

September 25, 2014

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。 +...

September 2, 2014

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是免費的。 +但如果不想安裝軟件的話,可以用 DevDocs。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。 +...

August 26, 2014

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$ 和 $MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。 另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。 -...

March 23, 2014

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有...

December 29, 2013

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。 +...

March 23, 2014

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有分頁功能的 console,和 Mac 的 iTerm 有點相似。它的特色就是可以讓你自由地設定各項細節。由字型、字體大小到啟動時開啟那一個目錄都能設定到。它除了可以調用 Windows 的 Command Prompt (cmd.exe) 之外,還可以調用 PowerShell、Bash 等等的 shell。如果用 Bash 的話可以用 Git 提供的版本,這個版本可以支援基本的 Bash 指令,用來執行 PHP CLI 程式、Grunt、PHPUnit 都有顏色顯示。 +...

December 29, 2013

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。 鏡頭鍍膜被刮傷,導致照片影像矇矓 ...

July 10, 2013

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! -...

June 8, 2013

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁...

June 6, 2013
June 8, 2013

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。 +...

June 6, 2013
+ PaperMod \ No newline at end of file diff --git a/posts/index.html b/posts/index.html index 1c3fbb12..066bb8cd 100644 --- a/posts/index.html +++ b/posts/index.html @@ -1,6 +1,19 @@ Posts | EricLog -

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像...

January 29, 2022
© 2024 EricLog +

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 Fragment、打開 Fragment 時要什麼參數和各 Fragment 之間如何導覽的 XML 檔案)。如果加上 Safe Args Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 Fragment 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。 +...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API。 +...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 +AndroidX WebKit AndroidX 其實有 WebKit 的 artifact androidx.webkit:webkit,但不是把整個 browser 加到 app 入面(WebView 實際在用的 web browser 是由 Google Play Store 提供,並可以 developer options 切換),而是把部分較新的 WebView 功能加個檢查 function,如果目前的 WebView 支援那個功能的話就可以執行這部分的 code。 +...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 +Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission FOREGROUND_SERVICE_DATA_SYNC 標明 foreground service 的目的。 +...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。 +...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。 +...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 nested navigation graph 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。 +...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 +Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 Activity 或者 Fragment。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式: +...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 Fragment 內的 onCreateView 加入 setContent 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。 +...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 intermediate data class)。 +...

January 29, 2022
+ PaperMod
\ No newline at end of file diff --git a/posts/index.xml b/posts/index.xml index 0f8204c5..fe156bea 100644 --- a/posts/index.xml +++ b/posts/index.xml @@ -1 +1,7 @@ -Posts on EricLoghttps://eric.swiftzer.net/posts/Recent content in Posts on EricLogHugo -- gohugo.iozh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 AndroidAndroid pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。AndroidX Room Relational Query Methodhttps://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/Sat, 29 Jan 2022 00:15:00 +0800https://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像 \ No newline at end of file +Posts on EricLoghttps://eric.swiftzer.net/posts/Recent content in Posts on EricLogHugo -- 0.139.2zh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/<p>AndroidX Navigation component 是 Google 推出的 <a href="https://www.youtube.com/watch?v=2k8x8V77CrU">single <code>Activity</code> app</a> navigation library。本身是用 <code>Fragment</code> 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 <code>Fragment</code>、打開 <code>Fragment</code> 時要什麼參數和各 <code>Fragment</code> 之間如何導覽的 XML 檔案)。如果加上 <a href="https://developer.android.com/guide/navigation/use-graph/safe-args">Safe Args</a> Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 <code>Fragment</code> 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。</p>Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/<p>如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 <a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send">HTTP v1 API</a>。</p>Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/<p>好幾年都沒有特別去用 Android 的 <code>WebView</code>,近期工作需要用到 <code>WebView</code>,所以特別去查一下並將資料放在這篇文章內方便日後翻查。</p> +<h2 id="androidx-webkit">AndroidX WebKit</h2> +<p>AndroidX 其實有 WebKit 的 artifact <a href="https://maven.google.com/web/index.html#androidx.webkit:webkit"><code>androidx.webkit:webkit</code></a>,但不是把整個 browser 加到 app 入面(<code>WebView</code> 實際在用的 web browser 是由 <a href="https://play.google.com/store/apps/details?id=com.google.android.webview">Google Play Store 提供</a>,並可以 developer options 切換),而是把部分較新的 <code>WebView</code> 功能加個檢查 function,如果目前的 <code>WebView</code> 支援那個功能的話就可以執行這部分的 code。</p>Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/<p>最近把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的 API level 升到 34 (<a href="https://developer.android.com/about/versions/14">Android 14</a>),中間發現了一些問題,在這裏記錄一下。</p> +<h2 id="foreground-service">Foreground service</h2> +<p>MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission <code>FOREGROUND_SERVICE_DATA_SYNC</code> 標明 foreground service 的目的。</p>Android pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/<p>在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。</p>Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/<p>最近抽點時間把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 參照 <a href="https://github.com/android/nowinandroid">Now in Android</a> 示範項目更新一下,例如改用 TOML 版的 <a href="https://docs.gradle.org/current/userguide/platforms.html">version catalog</a>(之前是用 Kotlin DSL)、轉用 <code>includeBuild</code> 加 convention plugin 取代之前把 plugin 放在 <code>buildSrc</code> 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。</p>Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/<p>這次<a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">遷移到 Compose</a> 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 <a href="https://developer.android.com/guide/navigation/navigation-nested-graphs">nested navigation graph</a> 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。</p>Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/<p><a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">上一篇</a>提過如何將 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。</p> +<h2 id="dependency-injection">Dependency injection</h2> +<p>按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 <code>Activity</code> 或者 <code>Fragment</code>。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式:</p>Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/<p>近幾個月斷斷續續替 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的界面由傳統 view system(即是 layout XML)轉為 <a href="https://developer.android.com/jetpack/compose">Jetpack Compose</a>,順帶補上去年參加 <a href="https://eric.swiftzer.net/2021-ithome-ironman/">iThome 鐵人賽</a>時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 <code>Fragment</code> 內的 <code>onCreateView</code> 加入 <code>setContent</code> 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。</p>AndroidX Room Relational Query Methodhttps://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/Sat, 29 Jan 2022 00:15:00 +0800https://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/<p>最近為 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 做新功能,剛好有個地方可以用到 <a href="https://developer.android.com/training/data-storage/room">Room</a> 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 <code>Map</code> 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 <a href="https://developer.android.com/training/data-storage/room/relationships#data-class">intermediate data class</a>)。</p> \ No newline at end of file diff --git a/posts/page/10/index.html b/posts/page/10/index.html index 1956e70a..350b7ec9 100644 --- a/posts/page/10/index.html +++ b/posts/page/10/index.html @@ -1,12 +1,19 @@ Posts | EricLog -

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 -...

June 4, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話...

May 27, 2013

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 +

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 +...

June 4, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話可以考慮用 Bitbucket。Bitbucket 最多可以與五個人共用私人的 repo,但它有推薦制度來增加共用名額,最多能與八個人共用同一個私人 repo。 +...

May 27, 2013

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 ...

March 24, 2013

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。 -...

January 12, 2013

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraN...

January 11, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

January 12, 2013

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。 +...

January 11, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。 +...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 ...

September 8, 2012

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 -...

September 3, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯...

August 4, 2012

散熱器新用法

舊電腦有個 Intel Pentium 4 原裝散熱器還未用過,非常新淨。拉絲鋁的材質看起來很前衛,拿來當信座都不錯。 信座正面 做法非常簡單: 將散熱器上的風扇拆走 在底部貼...

May 27, 2012
September 3, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。 +...

August 4, 2012

散熱器新用法

舊電腦有個 Intel Pentium 4 原裝散熱器還未用過,非常新淨。拉絲鋁的材質看起來很前衛,拿來當信座都不錯。 +信座正面 做法非常簡單: +將散熱器上的風扇拆走 在底部貼上雙面膠紙 貼上軟膠墊來防止散熱器刮花桌面 將信件插入散熱片之間的空隙即可 軟膠墊我是在先前買散裝 DVD 燒碟機的保護膠套中裁出來的。其實還可以用沙紙將邊角位磨到圓滑避免刮手。 +...

May 27, 2012
+ PaperMod \ No newline at end of file diff --git a/posts/page/11/index.html b/posts/page/11/index.html index 73c7cf16..5996ad69 100644 --- a/posts/page/11/index.html +++ b/posts/page/11/index.html @@ -1,7 +1,19 @@ Posts | EricLog -

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是...

April 27, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe...

March 31, 2012

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋...

January 4, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已...

December 5, 2011

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊...

May 6, 2011

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯...

April 22, 2011

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線...

April 11, 2011

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111...

June 27, 2010
© 2024 EricLog +

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是下載 iCal 檔的話就需要再下載並匯入香港公眾假期日曆過。 +...

April 27, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。 +...

March 31, 2012

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 AddThis 按鈕,實在令人難以相信這個是政府做的網站(就算是 gov.hk 都無這個做得出色)。 +...

January 4, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已經設定過權限,如果大家再發現日曆有問題的話請馬上通知,謝謝大家的支持! +...

December 5, 2011

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。 +...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊於 10 月 1 日,所以 10 月 1 日及 2 日都會是公眾假期。 +...

May 6, 2011

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 Android 2.2 更新會收不到 Google Chrome to Phone 的 Push notification 和 Moto 輸入法的問題。 +...

April 22, 2011

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。 +資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。 +...

April 11, 2011

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 +相片中白色的輕鐵列車就是新來港的 1117。 +Photoshop 扮移軸

June 27, 2010
+ PaperMod
\ No newline at end of file diff --git a/posts/page/12/index.html b/posts/page/12/index.html index ea189e81..65c2961d 100644 --- a/posts/page/12/index.html +++ b/posts/page/12/index.html @@ -1,10 +1,20 @@ Posts | EricLog -

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環...

March 11, 2010

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一...

January 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 由於目前有太少人可以試用 Google W...

October 24, 2009

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖...

May 2, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: +

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 +Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環、氹仔及澳門半島南部。拍攝出來的全景圖基本上都沒有大問題,只是有部分的模糊處理有錯。例如將巴士站牌、車身的非車牌部分都被模糊。較為有趣的相信是下圖的例子(位於天城路): +...

March 11, 2010

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一個路線圖程式。 +...

January 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 +Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave。Blip 就是一個個的訊息,亦即是在 Google Wave 對話的基本單位。一個 Wave 之中有不同的 Blip,其他成員可以就個別的 Blip 發表回應(訧像討論區的引用功能般)。那麼上一層的 Blip 就叫做 Wavelet。 +...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 +由於目前有太少人可以試用 Google Wave,所以暫時未能提供試用心得,日後將會補上。 +...

October 24, 2009

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。 +...

May 2, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: Microsoft OLE DB Provider for SQL Server error ‘80040e31’ Timeout expired /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -...

April 30, 2009

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 在 FontStruct 繪製字元「F」 FontStruct 是一個提供網...

April 8, 2009
April 30, 2009

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 +在 FontStruct 繪製字元「F」 FontStruct 是一個提供網上自製字型的服務。只需要在 FontStruct 免費注冊一個戶口,你就可以使用它以 Adobe Flex 製作的介面來繪製字元。別以為它是很難用,其實用法好像在使用小畫家般。只需要選好預製的形狀,然後在格網上畫上去便行了,網站首頁的影片亦有展示它的使用方法。 +...

April 8, 2009
+ PaperMod \ No newline at end of file diff --git a/posts/page/2/index.html b/posts/page/2/index.html index 1a0e4148..6e1bad49 100644 --- a/posts/page/2/index.html +++ b/posts/page/2/index.html @@ -1,7 +1,40 @@ Posts | EricLog -

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 文章目錄 終於來到最後一篇了!不經不覺已經寫了三十篇文章...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 文章目錄 我們終於來到第廿九篇,我們這次討論的題目都是之...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 文章目錄 終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最後一個功能:錯誤 banner。...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 文章目錄 我們這次會為班次頁加上自動更新和順帶為下一篇實...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 文章目錄 SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 文章目錄 由於我們在上一篇已經完成了成功載入班次的部分,...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最重要的頁面:抵站時間頁。這個頁...

October 6, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。 +...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 +文章目錄 +我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。 +...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。 +之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public: +...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。 +EtaPresenter 首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是: +...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockK 和 Strikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric。 +...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。 +...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 +文章目錄 +我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。 +我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值: +...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 +文章目錄 +SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。 +...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 +文章目錄 +由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。 +...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面: +...

October 6, 2021
+ PaperMod
\ No newline at end of file diff --git a/posts/page/3/index.html b/posts/page/3/index.html index 13ed813a..41665b65 100644 --- a/posts/page/3/index.html +++ b/posts/page/3/index.html @@ -1,7 +1,40 @@ Posts | EricLog -

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了 StationListAdap...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 文章目錄 最近兩篇都是講 navigation component,入面為了示...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 文章目錄 在 Android,navigation graph 是 resource 的...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 文章目錄 經過了兩個多星期後,我們終於開始進入 presentation layer 的部分...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 文章目錄 今天會為上一篇所寫的兩個 use case 加上 unit test。 GetLinesAndStationsUseCaseImplTest...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 文章目錄 經過這麼多集的 data layer 後,我們來到 domain layer。D...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 文章目錄 在繼續實作 domain layer 之前,我們會介紹一個方便日常開發...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 文章目錄 上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 文章目錄 今天會繼續寫 EtaResponseMapper...

September 26, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。 +...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 +文章目錄 +最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品: +...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml。 +eta.xml 在 project 的位置 先附上完整的內容,然後再慢慢講解入面的意思。 +...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 +文章目錄 +經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 Activity、Fragment、ViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。 +...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 +文章目錄 +今天會為上一篇所寫的兩個 use case 加上 unit test。 +GetLinesAndStationsUseCaseImplTest 這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepository 的 getLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。 +...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 +文章目錄 +經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 Activity、Fragment、ViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。 +...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 +文章目錄 +在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper。 +Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。 +...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。 +...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。 +...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 +文章目錄 +今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response: +...

September 26, 2021
+ PaperMod
\ No newline at end of file diff --git a/posts/page/4/index.html b/posts/page/4/index.html index d89524d7..014c8fb2 100644 --- a/posts/page/4/index.html +++ b/posts/page/4/index.html @@ -1,7 +1,40 @@ Posts | EricLog -

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 文章目錄 在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 文章目錄 上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Ins...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 文章目錄 上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 文章目錄 在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 文章目錄 在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 文章目錄 談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI l...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 文章目錄 JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 文章目錄 我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 文章目錄 Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 文章目錄 早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車...

September 16, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 +文章目錄 +在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(Strikt 和 MockK): +...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Instant、LocalDateTime 和 ZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。 +...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface: +interface Mapper<T, R> { suspend fun map(o: T): R } 有些 Android Clean Architecture 會每個 layer 都準備一個對應的 mapper interface,這次示範我們就簡化這一部分,全部 layer 都共用同一個 mapper interface,不論是由高層次 layer 去低層次 layer 還是由低層次 layer 去高層次 layer 都一樣。因為這個 mapper 都是為了在寫 unit test 時可以 mock 那個 interface 而不是 mock 那個 concrete implementation,所以它是不是共用 interface 問題不大。 +...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 +文章目錄 +在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。 +跨 layer 共用部分 不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。 +...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor。 +...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 +文章目錄 +談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。 +...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 +文章目錄 +JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,Gson、Moshi 和 Kotlin serialization 都是熱門的選擇。 +...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 +文章目錄 +我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。 +...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 +文章目錄 +Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture: +...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 +文章目錄 +早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。 +App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。 +...

September 16, 2021
+ PaperMod
\ No newline at end of file diff --git a/posts/page/5/index.html b/posts/page/5/index.html index c7578867..a71a2056 100644 --- a/posts/page/5/index.html +++ b/posts/page/5/index.html @@ -1,7 +1,19 @@ Posts | EricLog -

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取...

November 14, 2020

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話...

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 in...

August 13, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在...

April 27, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自...

February 29, 2020
© 2024 EricLog +

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而那個 Button 就固定在右邊。TextView 有 compound drawable,但如果想那個 icon 有 click listener 又用不了。所以最後都要分開 TextView 和 ImageView/ImageButton 兩個 view。 +...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後再把內文的驗證碼輸入到 app 中。如果想省卻用戶輪入文字的話有一些 app 會透過 READ_SMS 權限讀取 SMS 內容來抽取驗證碼,但 Google Play 已經限制非預設短訊 app 不可以有 READ_SMS 權限。 +...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各式 notification 的提示方法,例如有沒有音效、會不會彈出 heads-up notification 之類。如果 app 想自訂 notification 的聲音亦都要經 notification channel 設定(但用戶可以之後自行變更 notification channel 的音效) +...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法: +...

November 14, 2020

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。 +...

August 24, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 int、float、double 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。 +...

August 13, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。 +...

April 27, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。 +...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: +java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch Crashlytics Org Id > Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811 我的 app 是在 Firebase 開了兩個 project,一個是開發時用的,另一個是 production 用的。但只有 production 才有這個錯誤。如果把 release build 設為不上載 ProGuard/R8 mapping file 的話就不會有這個錯誤。 +...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 +如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。 +...

February 29, 2020
+ PaperMod
\ No newline at end of file diff --git a/posts/page/6/index.html b/posts/page/6/index.html index d91ed1fb..7312b60b 100644 --- a/posts/page/6/index.html +++ b/posts/page/6/index.html @@ -1,12 +1,22 @@ Posts | EricLog -

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散...

July 20, 2019

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 準備內容 上次我們用 2019 年香港公眾假期作例...

April 14, 2019

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 +

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon: +...

July 20, 2019

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 +準備內容 上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。 +...

April 14, 2019

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 Text facet 功能可以批量修改相近的資料,而不用寫程式 OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。 值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。 -...

February 20, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script...

October 17, 2018

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 +...

February 20, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。 +...

October 17, 2018

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。 -...

April 16, 2018

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular...

March 10, 2018

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boile...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserial...

September 16, 2017
April 16, 2018

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular expression 做檢查的話那句 regular expression 就會好長,而且要定時補上日後新推出的 TLD。 +...

March 10, 2018

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。 +@Parcelize data class Product(val name: String, val price: Double) : Parcelable 留意要在 build.gradle 加上: +...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。 +...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。 +...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable。 +...

September 16, 2017
+ PaperMod \ No newline at end of file diff --git a/posts/page/7/index.html b/posts/page/7/index.html index 86f354e2..26739127 100644 --- a/posts/page/7/index.html +++ b/posts/page/7/index.html @@ -1,12 +1,20 @@ Posts | EricLog -

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。Trai...

June 26, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、min...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門...

April 25, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 +

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。TrainBoard 提供港鐵將軍澳綫、東涌綫、機場快綫、迪士尼綫及西鐵綫列車的預計到達時間,而這個 app 是為 MTR Service Update 而做的。 +...

June 26, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 equals 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。 +...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。 +...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。 +...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門的方式來設定 release keystore 和密碼。而在 VCS checkout source code 後在 file system 補上 keystore.properties 和 keystore 未必可以在 CI 環境上做到。所以我將那個教學稍作改動,令到當 keystore.properties 不存在時就不提供 signing config,使它能 build 未加簽的 apk,然後才讓 CI 加簽 apk。 +...

April 25, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。 -...

April 22, 2017

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 -小米盒子國際版 ...

December 19, 2016

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖...

December 7, 2016

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。 +...

April 22, 2017

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。 +...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 +小米盒子國際版 ...

December 19, 2016

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 +nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 nodetree 指令就能印出目前目錄的結構。 +...

December 7, 2016

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。 除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。 ...

October 21, 2016
+ PaperMod \ No newline at end of file diff --git a/posts/page/8/index.html b/posts/page/8/index.html index 604faa88..7a8cccc5 100644 --- a/posts/page/8/index.html +++ b/posts/page/8/index.html @@ -1,12 +1,21 @@ Posts | EricLog -

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 -...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 -...

August 29, 2016

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 -...

July 8, 2016

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 Hexo 是一個用 Node 寫的 static site generator...

March 21, 2016

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是...

September 21, 2015

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 -...

August 30, 2015

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2...

May 17, 2015

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: +

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 +...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。 +...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 +...

August 29, 2016

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 +以下是所需工具: +有全手動模式的相機 魚眼鏡 / 廣角鏡 三腳架 Hugin Photoshop 能修改檔案 XMP 的工具 器材 按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。 +...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 +...

July 8, 2016

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 +Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code> 之類的 tag,用了不少時間才寫完。 +...

March 21, 2016

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。 +...

September 21, 2015

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 +...

August 30, 2015

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 +如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2,而下面就只揀選 ID3v2.3 UTF-16。而在 Remove 部分則剔選 ID3v1 及 APE。這樣就應該不會再有亂碼。 +...

May 17, 2015

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: CSS 要 inline,<style> 並不是全部 email client 都能支援 排版要用 <table> 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 ...

March 11, 2015
+ PaperMod \ No newline at end of file diff --git a/posts/page/9/index.html b/posts/page/9/index.html index e1354aa2..58b33eeb 100644 --- a/posts/page/9/index.html +++ b/posts/page/9/index.html @@ -1,13 +1,19 @@ Posts | EricLog -

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 +

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 -...

January 12, 2015

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤...

September 25, 2014

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,...

September 2, 2014

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是...

August 26, 2014

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$ 和 $MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。 +...

January 12, 2015

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。 +...

September 25, 2014

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。 +...

September 2, 2014

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是免費的。 +但如果不想安裝軟件的話,可以用 DevDocs。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。 +...

August 26, 2014

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$ 和 $MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。 另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。 -...

March 23, 2014

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有...

December 29, 2013

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。 +...

March 23, 2014

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有分頁功能的 console,和 Mac 的 iTerm 有點相似。它的特色就是可以讓你自由地設定各項細節。由字型、字體大小到啟動時開啟那一個目錄都能設定到。它除了可以調用 Windows 的 Command Prompt (cmd.exe) 之外,還可以調用 PowerShell、Bash 等等的 shell。如果用 Bash 的話可以用 Git 提供的版本,這個版本可以支援基本的 Bash 指令,用來執行 PHP CLI 程式、Grunt、PHPUnit 都有顏色顯示。 +...

December 29, 2013

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。 鏡頭鍍膜被刮傷,導致照片影像矇矓 ...

July 10, 2013

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! -...

June 8, 2013

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁...

June 6, 2013
June 8, 2013

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。 +...

June 6, 2013
+ PaperMod \ No newline at end of file diff --git "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.html" "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.html" index 029842c9..a80ab3c5 100644 --- "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.html" +++ "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.html" @@ -1,6 +1,39 @@ 2021 IThome 鐵人賽 | EricLog -

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 文章目錄 終於來到最後一篇了!不經不覺已經寫了三十篇文章...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 文章目錄 我們終於來到第廿九篇,我們這次討論的題目都是之...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 文章目錄 終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最後一個功能:錯誤 banner。...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 文章目錄 我們這次會為班次頁加上自動更新和順帶為下一篇實...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 文章目錄 SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 文章目錄 由於我們在上一篇已經完成了成功載入班次的部分,...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最重要的頁面:抵站時間頁。這個頁...

October 6, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。 +...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 +文章目錄 +我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。 +...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。 +之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public: +...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。 +EtaPresenter 首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是: +...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockK 和 Strikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric。 +...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。 +...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 +文章目錄 +我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。 +我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值: +...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 +文章目錄 +SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。 +...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 +文章目錄 +由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。 +...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面: +...

October 6, 2021
+ PaperMod
\ No newline at end of file diff --git "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.xml" "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.xml" index e2cb12a2..c435dbc6 100644 --- "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.xml" +++ "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/index.xml" @@ -1 +1,46 @@ -2021 IThome 鐵人賽 on EricLoghttps://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Recent content in 2021 IThome 鐵人賽 on EricLogHugo -- gohugo.iozh-twFri, 15 Oct 2021 00:00:00 +08002021 iThome 鐵人賽 Day 30:Wrapping uphttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-30-wrapping-up/Fri, 15 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-30-wrapping-up/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 文章目錄 終於來到最後一篇了!不經不覺已經寫了三十篇文章2021 iThome 鐵人賽 Day 29:Leftover topicshttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-29-leftover-topics/Thu, 14 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-29-leftover-topics/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 文章目錄 我們終於來到第廿九篇,我們這次討論的題目都是之2021 iThome 鐵人賽 Day 28:ETA screen testing (2)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/Wed, 13 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時2021 iThome 鐵人賽 Day 27:ETA screen testing (1)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/Tue, 12 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test2021 iThome 鐵人賽 Day 26:Station list screen testinghttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-26-station-list-screen-testing/Mon, 11 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-26-station-list-screen-testing/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 文章目錄 終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快2021 iThome 鐵人賽 Day 25:ETA screen (4)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-25-eta-screen-4/Sun, 10 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-25-eta-screen-4/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最後一個功能:錯誤 banner。2021 iThome 鐵人賽 Day 24:ETA screen (3)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-24-eta-screen-3/Sat, 09 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-24-eta-screen-3/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 文章目錄 我們這次會為班次頁加上自動更新和順帶為下一篇實2021 iThome 鐵人賽 Day 23:ETA screen (2)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-23-eta-screen-2/Fri, 08 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-23-eta-screen-2/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 文章目錄 SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出2021 iThome 鐵人賽 Day 22:Whistle proxyhttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-22-whistle-proxy/Thu, 07 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-22-whistle-proxy/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 文章目錄 由於我們在上一篇已經完成了成功載入班次的部分,2021 iThome 鐵人賽 Day 21:ETA screen (1)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-21-eta-screen-1/Wed, 06 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-21-eta-screen-1/本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最重要的頁面:抵站時間頁。這個頁 \ No newline at end of file +2021 IThome 鐵人賽 on EricLoghttps://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Recent content in 2021 IThome 鐵人賽 on EricLogHugo -- 0.139.2zh-twFri, 15 Oct 2021 00:00:00 +08002021 iThome 鐵人賽 Day 30:Wrapping uphttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-30-wrapping-up/Fri, 15 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-30-wrapping-up/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 30 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10281769">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。</p>2021 iThome 鐵人賽 Day 29:Leftover topicshttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-29-leftover-topics/Thu, 14 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-29-leftover-topics/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 29 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10281516">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。</p>2021 iThome 鐵人賽 Day 28:ETA screen testing (2)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/Wed, 13 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-28-eta-screen-testing-2/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 28 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10281130">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>上一篇我們寫了一些 <code>EtaViewModel</code> 的測試,這一篇會集中寫跟時間相關的測試。</p> +<p>之前在 <code>EtaViewModel</code> 我們定義了更新一次的間距常數 <code>AUTO_REFRESH_INTERVAL</code>,現在我們要在 <code>EtaViewModelTest</code> 用到它,所以要把它改成 public:</p>2021 iThome 鐵人賽 Day 27:ETA screen testing (1)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/Tue, 12 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-27-eta-screen-testing-1/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 27 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10280812">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。</p> +<h2 id="etapresenter"><code>EtaPresenter</code></h2> +<p>首先我們寫 <code>EtaPresenter</code> 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 <code>LineStationPresenterTest</code> 很不同。Parameterized test 的基本格式是:</p>2021 iThome 鐵人賽 Day 26:Station list screen testinghttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-26-station-list-screen-testing/Mon, 11 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-26-station-list-screen-testing/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 26 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10280351">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>終於來到為 <code>ViewModel</code> 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 <a href="https://mockk.io/">MockK</a> 和 <a href="https://strikt.io/">Strikt</a>。來到現在偏向 UI 那邊的 unit test,我們會用到 <a href="http://robolectric.org/">Robolectric</a>。</p>2021 iThome 鐵人賽 Day 25:ETA screen (4)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-25-eta-screen-4/Sun, 10 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-25-eta-screen-4/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 25 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10279995">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。</p>2021 iThome 鐵人賽 Day 24:ETA screen (3)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-24-eta-screen-3/Sat, 09 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-24-eta-screen-3/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 24 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10279594">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。</p> +<p>我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 <a href="https://data.gov.hk/tc-data/dataset/mtr-data2-nexttrain-data">API 的介紹</a>,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值:</p>2021 iThome 鐵人賽 Day 23:ETA screen (2)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-23-eta-screen-2/Fri, 08 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-23-eta-screen-2/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 23 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10279171">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<h2 id="savedstatehandle"><code>SavedStateHandle</code></h2> +<p>不知道大家有沒有發現在「ETA Screen (1)」貼出來的 <code>EtaViewModel</code> 的 constructor 有一個 <code>SavedStateHandle</code>?在繼續完成餘下的錯誤情景前,我們先看看 <code>SavedStateHandle</code> 是甚麼。</p>2021 iThome 鐵人賽 Day 22:Whistle proxyhttps://eric.swiftzer.net/2021/10/2021-ithome-ironman-22-whistle-proxy/Thu, 07 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-22-whistle-proxy/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 22 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10278714">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。</p>2021 iThome 鐵人賽 Day 21:ETA screen (1)https://eric.swiftzer.net/2021/10/2021-ithome-ironman-21-eta-screen-1/Wed, 06 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/2021/10/2021-ithome-ironman-21-eta-screen-1/<blockquote> +<p>本篇文章是 <a href="https://ithelp.ithome.com.tw/2021ironman">2021 iThome 鐵人賽</a>參賽題目「<a href="https://ithelp.ithome.com.tw/users/20139666/ironman/4661">寫一個列車抵站時間 Android App</a>」的第 21 篇,你可到 iThome <a href="https://ithelp.ithome.com.tw/articles/10278274">查看原文</a>。</p> +<p><a href="https://eric.swiftzer.net/2021-ithome-ironman/">文章目錄</a></p> +</blockquote> +<p>現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 <code>RecyclerView</code> 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面:</p> \ No newline at end of file diff --git "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/2/index.html" "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/2/index.html" index 9c9de27c..0c916b3d 100644 --- "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/2/index.html" +++ "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/2/index.html" @@ -1,7 +1,40 @@ 2021 IThome 鐵人賽 | EricLog -

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了 StationListAdap...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 文章目錄 最近兩篇都是講 navigation component,入面為了示...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 文章目錄 在 Android,navigation graph 是 resource 的...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 文章目錄 經過了兩個多星期後,我們終於開始進入 presentation layer 的部分...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 文章目錄 今天會為上一篇所寫的兩個 use case 加上 unit test。 GetLinesAndStationsUseCaseImplTest...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 文章目錄 經過這麼多集的 data layer 後,我們來到 domain layer。D...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 文章目錄 在繼續實作 domain layer 之前,我們會介紹一個方便日常開發...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 文章目錄 上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 文章目錄 今天會繼續寫 EtaResponseMapper...

September 26, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。 +...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 +文章目錄 +最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品: +...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml。 +eta.xml 在 project 的位置 先附上完整的內容,然後再慢慢講解入面的意思。 +...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 +文章目錄 +經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 Activity、Fragment、ViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。 +...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 +文章目錄 +今天會為上一篇所寫的兩個 use case 加上 unit test。 +GetLinesAndStationsUseCaseImplTest 這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepository 的 getLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。 +...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 +文章目錄 +經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 Activity、Fragment、ViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。 +...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 +文章目錄 +在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper。 +Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。 +...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。 +...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。 +...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 +文章目錄 +今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response: +...

September 26, 2021
+ PaperMod
\ No newline at end of file diff --git "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/3/index.html" "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/3/index.html" index 33d96f5b..aff60d10 100644 --- "a/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/3/index.html" +++ "b/tags/2021-ithome-\351\220\265\344\272\272\350\263\275/page/3/index.html" @@ -1,6 +1,39 @@ 2021 IThome 鐵人賽 | EricLog -

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 文章目錄 在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 文章目錄 上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Ins...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 文章目錄 上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 文章目錄 在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 文章目錄 在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 文章目錄 談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI l...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 文章目錄 JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 文章目錄 我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 文章目錄 Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 文章目錄 早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車...

September 16, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 +文章目錄 +在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(Strikt 和 MockK): +...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Instant、LocalDateTime 和 ZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。 +...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface: +interface Mapper<T, R> { suspend fun map(o: T): R } 有些 Android Clean Architecture 會每個 layer 都準備一個對應的 mapper interface,這次示範我們就簡化這一部分,全部 layer 都共用同一個 mapper interface,不論是由高層次 layer 去低層次 layer 還是由低層次 layer 去高層次 layer 都一樣。因為這個 mapper 都是為了在寫 unit test 時可以 mock 那個 interface 而不是 mock 那個 concrete implementation,所以它是不是共用 interface 問題不大。 +...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 +文章目錄 +在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。 +跨 layer 共用部分 不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。 +...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor。 +...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 +文章目錄 +談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。 +...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 +文章目錄 +JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,Gson、Moshi 和 Kotlin serialization 都是熱門的選擇。 +...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 +文章目錄 +我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。 +...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 +文章目錄 +Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture: +...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 +文章目錄 +早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。 +App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。 +...

September 16, 2021
+ PaperMod
\ No newline at end of file diff --git a/tags/android/index.html b/tags/android/index.html index fc13c4cf..ce330932 100644 --- a/tags/android/index.html +++ b/tags/android/index.html @@ -1,6 +1,19 @@ Android | EricLog -

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像...

January 29, 2022
© 2024 EricLog +

AndroidX Navigation component for Jetpack Compose type safety

AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 Fragment、打開 Fragment 時要什麼參數和各 Fragment 之間如何導覽的 XML 檔案)。如果加上 Safe Args Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 Fragment 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。 +...

August 18, 2024

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API。 +...

June 1, 2024

Android WebView 筆記

好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 +AndroidX WebKit AndroidX 其實有 WebKit 的 artifact androidx.webkit:webkit,但不是把整個 browser 加到 app 入面(WebView 實際在用的 web browser 是由 Google Play Store 提供,並可以 developer options 切換),而是把部分較新的 WebView 功能加個檢查 function,如果目前的 WebView 支援那個功能的話就可以執行這部分的 code。 +...

February 24, 2024

Android 14 migration

最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 +Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission FOREGROUND_SERVICE_DATA_SYNC 標明 foreground service 的目的。 +...

February 2, 2024

Android pseudolocale

在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。 +...

July 14, 2023

Android 13 Per-app Language Preferences

最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。 +...

January 25, 2023

Jetpack Compose Navigation component sub-graph

這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 nested navigation graph 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。 +...

July 27, 2022

Jetpack Compose 遷移 (2)

上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 +Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 Activity 或者 Fragment。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式: +...

July 26, 2022

Jetpack Compose 遷移 (1)

近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 Fragment 內的 onCreateView 加入 setContent 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。 +...

July 24, 2022

AndroidX Room Relational Query Method

最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 intermediate data class)。 +...

January 29, 2022
+ PaperMod
\ No newline at end of file diff --git a/tags/android/index.xml b/tags/android/index.xml index 9fc00946..f92cc488 100644 --- a/tags/android/index.xml +++ b/tags/android/index.xml @@ -1 +1,7 @@ -Android on EricLoghttps://eric.swiftzer.net/tags/android/Recent content in Android on EricLogHugo -- gohugo.iozh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/AndroidX Navigation component 是 Google 推出的 single Activity app navigation library。本身是用 Fragment 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/好幾年都沒有特別去用 Android 的 WebView,近期工作需要用到 WebView,所以特別去查一下並將資料放在這篇文章內方便日後翻查。 AndroidX WebKit AndroidX 其實有 WebKit 的Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/最近把 MetroRide 的 API level 升到 34 (Android 14),中間發現了一些問題,在這裏記錄一下。 Foreground service MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 AndroidAndroid pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/最近抽點時間把 MetroRide 參照 Now in Android 示範項目更新一下,例如改用 TOML 版的 version catalog(之前是用 Kotlin DSL)、轉用 includeBuild 加 convention plugin 取代之前把 plugin 放在 buildSrc 內、更新 dependency 版本Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/這次遷移到 Compose 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/上一篇提過如何將 MetroRide 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。 Dependency injection 按照官方的建議,composable function 要用到的 dependency 應該由Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/近幾個月斷斷續續替 MetroRide 的界面由傳統 view system(即是 layout XML)轉為 Jetpack Compose,順帶補上去年參加 iThome 鐵人賽時用來做示範的重鐵抵站時間功能。AndroidX Room Relational Query Methodhttps://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/Sat, 29 Jan 2022 00:15:00 +0800https://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/最近為 MetroRide 做新功能,剛好有個地方可以用到 Room 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 Map 一次過 return 出來,不用像 \ No newline at end of file +Android on EricLoghttps://eric.swiftzer.net/tags/android/Recent content in Android on EricLogHugo -- 0.139.2zh-twSun, 18 Aug 2024 22:00:00 +0800AndroidX Navigation component for Jetpack Compose type safetyhttps://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/2024/08/androidx-navigation-compose-type-safety/<p>AndroidX Navigation component 是 Google 推出的 <a href="https://www.youtube.com/watch?v=2k8x8V77CrU">single <code>Activity</code> app</a> navigation library。本身是用 <code>Fragment</code> 來做每一頁的內容,然後再用新的 Android resource type——navigation 來定義 navigation graph(即是聲明一個 navigation graph 內有什麼 <code>Fragment</code>、打開 <code>Fragment</code> 時要什麼參數和各 <code>Fragment</code> 之間如何導覽的 XML 檔案)。如果加上 <a href="https://developer.android.com/guide/navigation/use-graph/safe-args">Safe Args</a> Gradle plugin 的話就會按 navigation graph XML 檔案生成那些 Java code 去讓你在 <code>Fragment</code> 內轉頁時調用,那就不會怕轉頁時漏了幾個參數沒有傳到,因為漏了的話就不能成功 compile。</p>Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/<p>如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 <a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send">HTTP v1 API</a>。</p>Android WebView 筆記https://eric.swiftzer.net/2024/02/android-webview/Sat, 24 Feb 2024 11:45:00 +0800https://eric.swiftzer.net/2024/02/android-webview/<p>好幾年都沒有特別去用 Android 的 <code>WebView</code>,近期工作需要用到 <code>WebView</code>,所以特別去查一下並將資料放在這篇文章內方便日後翻查。</p> +<h2 id="androidx-webkit">AndroidX WebKit</h2> +<p>AndroidX 其實有 WebKit 的 artifact <a href="https://maven.google.com/web/index.html#androidx.webkit:webkit"><code>androidx.webkit:webkit</code></a>,但不是把整個 browser 加到 app 入面(<code>WebView</code> 實際在用的 web browser 是由 <a href="https://play.google.com/store/apps/details?id=com.google.android.webview">Google Play Store 提供</a>,並可以 developer options 切換),而是把部分較新的 <code>WebView</code> 功能加個檢查 function,如果目前的 <code>WebView</code> 支援那個功能的話就可以執行這部分的 code。</p>Android 14 migrationhttps://eric.swiftzer.net/2024/02/android-14-migration/Fri, 02 Feb 2024 00:05:00 +0800https://eric.swiftzer.net/2024/02/android-14-migration/<p>最近把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的 API level 升到 34 (<a href="https://developer.android.com/about/versions/14">Android 14</a>),中間發現了一些問題,在這裏記錄一下。</p> +<h2 id="foreground-service">Foreground service</h2> +<p>MetroRide 有用到 AndroidX WorkManager 來下載離線資料並放到 SQLite database 內,而且開了 foreground service。在 Android 14 規定要加 permission <code>FOREGROUND_SERVICE_DATA_SYNC</code> 標明 foreground service 的目的。</p>Android pseudolocalehttps://eric.swiftzer.net/2023/07/android-pseudolocale/Fri, 14 Jul 2023 23:40:00 +0800https://eric.swiftzer.net/2023/07/android-pseudolocale/<p>在處理 app UI 多國語言時,我們不時要留意是不是預留了足夠空間來顯示文字。一般而言,中文內容通常都比其他語言短,你的 UI 可能看起來沒有問題,但換到其他寫得比較長的語言就可能不夠位顯示。但在開發初期可能還未開始翻譯,只有英文版,未必能在早期察覺這個問題。</p>Android 13 Per-app Language Preferenceshttps://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/Wed, 25 Jan 2023 14:27:00 +0800https://eric.swiftzer.net/2023/01/android-13-per-app-language-preferences/<p>最近抽點時間把 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 參照 <a href="https://github.com/android/nowinandroid">Now in Android</a> 示範項目更新一下,例如改用 TOML 版的 <a href="https://docs.gradle.org/current/userguide/platforms.html">version catalog</a>(之前是用 Kotlin DSL)、轉用 <code>includeBuild</code> 加 convention plugin 取代之前把 plugin 放在 <code>buildSrc</code> 內、更新 dependency 版本和把 target SDK 升到最新(即是 Android 13;API level 33)。這次想分享的是適配 Android 13 的 per-app language preferences(個別應用程式語言偏好)功能。</p>Jetpack Compose Navigation component sub-graphhttps://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/Wed, 27 Jul 2022 21:30:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-navigation-component-sub-graph/<p>這次<a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">遷移到 Compose</a> 時特別花了時間試用 Compose 的 Navigation component,終於弄清 nested graph 的意義。其實 Compose 的 Navigation component 底層都是跟 XML 版的 Navigation component 一樣,只是底層多了以 route 形式的處理。以往的說明文件在介紹 <a href="https://developer.android.com/guide/navigation/navigation-nested-graphs">nested navigation graph</a> 時沒有太具體說明 nested graph 背後的意義,看完之後可能覺得只是用來避免單一 XML 檔過長而拆成不同 sub-graph。但其實在 deep link 時是有特別意義。</p>Jetpack Compose 遷移 (2)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/Tue, 26 Jul 2022 14:00:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-2/<p><a href="https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/">上一篇</a>提過如何將 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 由傳統 view system 遷移到 Jetpack Compose。但一篇又太長,所以分拆成兩篇。</p> +<h2 id="dependency-injection">Dependency injection</h2> +<p>按照官方的建議,composable function 要用到的 dependency 應該由 caller 經參數提供。然後就是由外層一直傳進去。至於那個外層最遠可以去到 <code>Activity</code> 或者 <code>Fragment</code>。由於 composable function 就是 top-level function,沒有 class 包住,所以平常用開的 Dagger 或者 Koin 之類的 DI library 都無辦法輕易地在 composable function 經 DI 拿到 dependency。如果以 Dagger Hilt 來計,目前是有這幾種方式:</p>Jetpack Compose 遷移 (1)https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/Sun, 24 Jul 2022 14:06:00 +0800https://eric.swiftzer.net/2022/07/jetpack-compose-migration-1/<p>近幾個月斷斷續續替 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 的界面由傳統 view system(即是 layout XML)轉為 <a href="https://developer.android.com/jetpack/compose">Jetpack Compose</a>,順帶補上去年參加 <a href="https://eric.swiftzer.net/2021-ithome-ironman/">iThome 鐵人賽</a>時用來做示範的重鐵抵站時間功能。昨天新版 app 上架了就來分享一下遷移過程。其實這個 app 在之前的版本有把其中一頁靜態的頁面(延誤定義)改用 Compose,那時是在 <code>Fragment</code> 內的 <code>onCreateView</code> 加入 <code>setContent</code> 顯示 Compose 內容。由於那時只是做排版,沒有遇到大問題。之後開始慢慢轉用 Compose 才遇到問題。</p>AndroidX Room Relational Query Methodhttps://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/Sat, 29 Jan 2022 00:15:00 +0800https://eric.swiftzer.net/2022/01/androidx-room-relational-query-method/<p>最近為 <a href="https://play.google.com/store/apps/details?id=net.swiftzer.metroride">MetroRide</a> 做新功能,剛好有個地方可以用到 <a href="https://developer.android.com/training/data-storage/room">Room</a> 2.4 的新功能:Relational Query Method。這個功能可以把平常 table 之間的 relationship 用 <code>Map</code> 一次過 return 出來,不用像以前般要特製一個專門的 data class 來做 DAO query method 的 return type(正式名稱叫做 <a href="https://developer.android.com/training/data-storage/room/relationships#data-class">intermediate data class</a>)。</p> \ No newline at end of file diff --git a/tags/android/page/2/index.html b/tags/android/page/2/index.html index 38399d9b..c9b580ff 100644 --- a/tags/android/page/2/index.html +++ b/tags/android/page/2/index.html @@ -1,7 +1,40 @@ Android | EricLog -

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 文章目錄 終於來到最後一篇了!不經不覺已經寫了三十篇文章...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 文章目錄 我們終於來到第廿九篇,我們這次討論的題目都是之...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 文章目錄 終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最後一個功能:錯誤 banner。...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 文章目錄 我們這次會為班次頁加上自動更新和順帶為下一篇實...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 文章目錄 SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 文章目錄 由於我們在上一篇已經完成了成功載入班次的部分,...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 文章目錄 現在來到整個 app 最重要的頁面:抵站時間頁。這個頁...

October 6, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 30:Wrapping up

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 30 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到最後一篇了!不經不覺已經寫了三十篇文章。我們由 Ktor client 接駁 API 一直講到 UI,然後再做 ViewModel 的 unit testing。中間加插了時間處理、Flipper 和 proxy server 的內容。其實這些內容有部分是以前工作上跟 Android 同事定期會議分享的內容。那時已經有想法把內容放到自己的 blog 上,但最後只是放了小許。現在有這個機會就加插這些內容進去。除了用來填滿三十篇之外,就是把一些不會直接在 Android 開發教學找到但又實用的東西放進去。我在八月尾才決定題目,然後開始寫開首的文章,並且準備示範 project 的 code。初初寫的時候以為三十篇是很多,所以開頭寫的內容不夠充實。但到了多 code snippet 的部分就發覺光是 code 就很長,要分拆做好幾篇。所以三十篇入面光是不同的 unit testing 都佔了十篇。由於我是一邊寫文一邊準備示範 project,所以內容分配是頭輕尾重,尤其是後段做 UI 的部分一篇的長度比開首的文章長幾倍。還有是內容可能有時會跟前一兩篇重覆(好像 Dagger 某些內容有重覆提及)。本來還打算加插 Compose 的內容但發覺剩餘篇數太少而且寫完都不夠完整,所以改做 ViewModel 測試作罷。 +...

October 15, 2021

2021 iThome 鐵人賽 Day 29:Leftover topics

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 29 篇,你可到 iThome 查看原文。 +文章目錄 +我們終於來到第廿九篇,我們這次討論的題目都是之前討論過的東西的延伸。因為篇幅和時間有限就只好把它們合併成一篇。 +...

October 14, 2021

2021 iThome 鐵人賽 Day 28:ETA screen testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 28 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫了一些 EtaViewModel 的測試,這一篇會集中寫跟時間相關的測試。 +之前在 EtaViewModel 我們定義了更新一次的間距常數 AUTO_REFRESH_INTERVAL,現在我們要在 EtaViewModelTest 用到它,所以要把它改成 public: +...

October 13, 2021

2021 iThome 鐵人賽 Day 27:ETA screen testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 27 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了車站列表頁的 ViewModel 和 Presenter 的 unit test。現在轉過去寫班次頁的 unit test。 +EtaPresenter 首先我們寫 EtaPresenter 的 test。這次我們來點新意思:使用 JUnit 4 的 parameterized test,寫法跟之前 LineStationPresenterTest 很不同。Parameterized test 的基本格式是: +...

October 12, 2021

2021 iThome 鐵人賽 Day 26:Station list screen testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 26 篇,你可到 iThome 查看原文。 +文章目錄 +終於來到為 ViewModel 寫 unit test 的部分,亦都意味着這個系列快要完結。之前我們寫過其他 layer 的 unit test,用過 MockK 和 Strikt。來到現在偏向 UI 那邊的 unit test,我們會用到 Robolectric。 +...

October 11, 2021

2021 iThome 鐵人賽 Day 25:ETA screen (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 25 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最後一個功能:錯誤 banner。這個 banner 出現的目的是因為鐵路隧道沿綫的電話上網訊號都接收得不太好(因為太多人同時在用),很容易出現錯誤。如果自動更新時有不能上網的錯誤會彈出全頁錯誤畫面的話效果就不太好。所以就設計了 banner 形式的顯示錯誤方式。 +...

October 10, 2021

2021 iThome 鐵人賽 Day 24:ETA screen (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 24 篇,你可到 iThome 查看原文。 +文章目錄 +我們這次會為班次頁加上自動更新和順帶為下一篇實作錯誤 banner 做準備。 +我們這頁除非顯示不能連接到互聯網這類錯誤外,都不會出現重新載入按鈕,這是因為這頁就應該自動更新。按照 API 的介紹,它是每十秒更新一次。我們先準備一個 constant 來表示這個數值: +...

October 9, 2021

2021 iThome 鐵人賽 Day 23:ETA screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 23 篇,你可到 iThome 查看原文。 +文章目錄 +SavedStateHandle 不知道大家有沒有發現在「ETA Screen (1)」貼出來的 EtaViewModel 的 constructor 有一個 SavedStateHandle?在繼續完成餘下的錯誤情景前,我們先看看 SavedStateHandle 是甚麼。 +...

October 8, 2021

2021 iThome 鐵人賽 Day 22:Whistle proxy

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 22 篇,你可到 iThome 查看原文。 +文章目錄 +由於我們在上一篇已經完成了成功載入班次的部分,接下來要做的當然是不正常的情況。雖然港鐵間中會有事故,但都可遇不可求。要檢查我們做的東西是不是正確除了寫自動化測試之外,既然我們都做到 UI 的部分那就當然要直接看實物更好吧。所以我們先換一換題目討論 proxy server。 +...

October 7, 2021

2021 iThome 鐵人賽 Day 21:ETA screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 21 篇,你可到 iThome 查看原文。 +文章目錄 +現在來到整個 app 最重要的頁面:抵站時間頁。這個頁面基本上都是跟上一頁一樣,都是以 RecyclerView 為主。但因為這次的內容要從 API server 取得,即是說我們需要處理載入中、載入成功和載入失敗三個情景。當中載入成功時更要細分為顯示班次、事故和延誤三個情景。情況好像有點複雜,我們先看看各情景時的畫面: +...

October 6, 2021
+ PaperMod
\ No newline at end of file diff --git a/tags/android/page/3/index.html b/tags/android/page/3/index.html index aa0654e0..63ab8c3e 100644 --- a/tags/android/page/3/index.html +++ b/tags/android/page/3/index.html @@ -1,7 +1,40 @@ Android | EricLog -

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們完成了 StationListAdap...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 文章目錄 最近兩篇都是講 navigation component,入面為了示...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 文章目錄 在 Android,navigation graph 是 resource 的...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 文章目錄 經過了兩個多星期後,我們終於開始進入 presentation layer 的部分...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 文章目錄 今天會為上一篇所寫的兩個 use case 加上 unit test。 GetLinesAndStationsUseCaseImplTest...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 文章目錄 經過這麼多集的 data layer 後,我們來到 domain layer。D...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 文章目錄 在繼續實作 domain layer 之前,我們會介紹一個方便日常開發...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 文章目錄 上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 文章目錄 上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 文章目錄 今天會繼續寫 EtaResponseMapper...

September 26, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 20:Station list screen (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 20 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們完成了 StationListAdapter,我們現在會繼續車站列表的 UI 部分。 +...

October 5, 2021

2021 iThome 鐵人賽 Day 19:Station list screen (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 19 篇,你可到 iThome 查看原文。 +文章目錄 +最近兩篇都是講 navigation component,入面為了示範設定 navigation 我們已經預先準備了兩頁的 Fragment class 和 layout XML,這樣我們之後就不用再跳去設定 navigation 的東西。現在開始會開始正式實作 app 的界面部分。我們會由車站列表頁開始實作,現在看看完成品: +...

October 4, 2021

2021 iThome 鐵人賽 Day 18:Navigation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 18 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android,navigation graph 是 resource 的一種,我們先建立 eta.xml。 +eta.xml 在 project 的位置 先附上完整的內容,然後再慢慢講解入面的意思。 +...

October 3, 2021

2021 iThome 鐵人賽 Day 17:Navigation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 17 篇,你可到 iThome 查看原文。 +文章目錄 +經過了兩個多星期後,我們終於開始進入 presentation layer 的部分。Presentation layer 就是做 UI 相關的東西,例如 Activity、Fragment、ViewModel 這些 class。而這次要做的部分是要準備基本的 navigation。 +...

October 2, 2021

2021 iThome 鐵人賽 Day 16:Domain layer testing

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 16 篇,你可到 iThome 查看原文。 +文章目錄 +今天會為上一篇所寫的兩個 use case 加上 unit test。 +GetLinesAndStationsUseCaseImplTest 這個 test 其實很簡單,因為本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我們只需 mock EtaRepository 的 getLinesAndStations 然後檢查 use case return 出來的 map 是不是跟我們 mock 出來的 getLinesAndStations return value 是否一致即可。 +...

October 1, 2021

2021 iThome 鐵人賽 Day 15:Domain layer implementation

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 15 篇,你可到 iThome 查看原文。 +文章目錄 +經過這麼多集的 data layer 後,我們來到 domain layer。Domain layer 的用途是用來放 business logic,並向 presentation layer(即是 Activity、Fragment、ViewModel、layout XML 這層)提供一個表象 (façade) 去用跟 data 互動。你或許會問為甚麼我們不在 ViewModel 直接 call 之前寫好的 repository 而要經 domain layer,這是為了日後功能變更提供彈性。例如一個新聞 app 會有新聞列表頁跟新聞正文頁,當按下新聞列表的項目時就會進入正文頁。在正文頁按了收藏後返回列表頁就會發現剛才那個項目出現了一個收藏 icon。如果在 data layer 跟 presentation layer 之間直接接駁的話,那個列表頁在收藏狀態變動後自動更新的 logic 就會放入 ViewModel 內。(可能是正文頁在按下收藏 icon 時用 event bus 通知其他 ViewModel 去更新 UI。)如果再多幾個地方跟那個收藏狀態有關連的話那些觸發檢查更新的 code 就會放在各個 ViewModel 之中,日後收藏功能再有改動就很麻煩。另外,一些複雜的東西例如之前提及過的車費計算都不是單純在網路上 call API 然後稍加修飾就輸出去 UI 上,而是真的有 business logic 在 mobile app 內進行。那些 business logic 都是會放在 domain layer 入面。可能車費計算背後有很多的 class,但我們只外露幾個 use case 或者 interactor 讓 presentation layer call,這樣就把背後複雜的東西隱藏起來。 +...

September 30, 2021

2021 iThome 鐵人賽 Day 14:Flipper

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 14 篇,你可到 iThome 查看原文。 +文章目錄 +在繼續實作 domain layer 之前,我們會介紹一個方便日常開發的工具:Flipper。 +Android Studio 有個功能是查看 HTTP request 和 UI layout,但有時不太方便。如果是查看 HTTP request 的話,有些人會用 proxy server 來截取 HTTP request 和 response。但有個問題是裝置要先安裝 proxy server 的 root certificate,而且部分 app 或 SDK 會做 cert pinning,駁了 proxy server 就用不到那些 app 或 SDK(Google Places SDK 會有這個問題)。 +...

September 29, 2021

2021 iThome 鐵人賽 Day 13:Data layer testing (4)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 13 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇示範了 Ktor mock engine 的設定和測試了如果出現 exception 時能否順利地處理。現在就測試 getEta 輸出班次的情景。 +...

September 28, 2021

2021 iThome 鐵人賽 Day 12:Data layer testing (3)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 12 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇我們寫好了 EtaResponseMapper 的 unit test。但 data layer 還有 EtaResponseMapper 未寫 unit test。今天我們就寫這一個 class 的 unit test。 +...

September 27, 2021

2021 iThome 鐵人賽 Day 11:Data layer testing (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 11 篇,你可到 iThome 查看原文。 +文章目錄 +今天會繼續寫 EtaResponseMapperTest。我們示範的 test case 是正常輸出班次的情景。首先是準備 response: +...

September 26, 2021
+ PaperMod
\ No newline at end of file diff --git a/tags/android/page/4/index.html b/tags/android/page/4/index.html index 6991ba1c..36a0fc07 100644 --- a/tags/android/page/4/index.html +++ b/tags/android/page/4/index.html @@ -1,7 +1,40 @@ Android | EricLog -

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 文章目錄 在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 文章目錄 上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Ins...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 文章目錄 上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 文章目錄 在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 文章目錄 在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 文章目錄 談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI l...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 文章目錄 JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 文章目錄 我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 文章目錄 Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 文章目錄 早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車...

September 16, 2021
© 2024 EricLog +

2021 iThome 鐵人賽 Day 10:Data layer testing (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 10 篇,你可到 iThome 查看原文。 +文章目錄 +在切回去寫 domain layer 之前,我們先把之前寫好的 data layer class 補回 unit test。在開始寫之前,我們要先加入一些 testing 會用到的 dependency(Strikt 和 MockK): +...

September 25, 2021

2021 iThome 鐵人賽 Day 9:Date & time

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 9 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇在實作 EtaResponseMapper 的時候我們用了 Java 8 開始有的 Instant、LocalDateTime 和 ZonedDateTime。它們都是跟日期時間相關的 class。但其實 Kotlin 都有 kotlinx-datetime 做類似的東西。但目前 kotlinx-datetime 還是在早期開發階段,有很多常用功能都未做到,例如我們這次需要用到的 formatter 目前仍需要用 Java time,所以還是用 Java time 算。 +...

September 24, 2021

2021 iThome 鐵人賽 Day 8:Data layer implementation (2)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 8 篇,你可到 iThome 查看原文。 +文章目錄 +上一篇的 repository 還欠一個 mapper 把 EtaResponse 轉成 EtaResult。我們首先準備一個通用的 interface: +interface Mapper<T, R> { suspend fun map(o: T): R } 有些 Android Clean Architecture 會每個 layer 都準備一個對應的 mapper interface,這次示範我們就簡化這一部分,全部 layer 都共用同一個 mapper interface,不論是由高層次 layer 去低層次 layer 還是由低層次 layer 去高層次 layer 都一樣。因為這個 mapper 都是為了在寫 unit test 時可以 mock 那個 interface 而不是 mock 那個 concrete implementation,所以它是不是共用 interface 問題不大。 +...

September 23, 2021

2021 iThome 鐵人賽 Day 7:Data layer implementation (1)

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 7 篇,你可到 iThome 查看原文。 +文章目錄 +在上一篇,我們把 Ktor client 加到 Dagger 的 object graph 內。現在我們就繼續寫 data layer 部分。 +跨 layer 共用部分 不過在繼續之前,我們要先準備一些通用的 enum。這些 enum 分別是用來表示路綫、車站和語言。因為這次的示範 app 所用到的車站和路綫的數量有限,我們就簡化用 enum 寫死在 app 入面就算了,但如果數量多的話或許會改用 SQLite database 之類去儲存它們。在這種情況下,我們一般都會每個 layer 都有對應的 data class 表示,而不會好像現在把 data class/enum 放在 common 的地方。 +...

September 22, 2021

2021 iThome 鐵人賽 Day 6:HTTP Client

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 6 篇,你可到 iThome 查看原文。 +文章目錄 +在 Android 開發如果要用到 HTTP client 的話基本上大家都預設用 OkHttp + Retrofit 這個組合。這次我們試試一些新東西:Ktor。 +...

September 21, 2021

2021 iThome 鐵人賽 Day 5:Dependency injection

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 5 篇,你可到 iThome 查看原文。 +文章目錄 +談到 Android 的 dependency injection (DI),大家一定會想到 Dagger 這個 DI library。因為 Dagger 2 是由 Google 開發,加上在 Android Developers 網站有對應的教學文章,所以成為不二之選。不過 Dagger 向來都有難學的印象,尤其是在以前 Dagger 2 網站那個惡名昭彰的咖啡機教學。本來用咖啡機來說明 DI 的概念就沒有甚麼大問題,問題在於看完那個咖啡機類比是不會知道如何在 Android app 上套用那些概念和 Dagger 提供的功能。或許大家讀電腦科學/計算機科學/資訊工程之類的課程時都有學過 DI 但還是不能順利地用 Dagger,這是因為 Android 那些組件的 constructor 不是由我們去 call,結果要在 onCreate 之類的 callback 加上 DI library 要我們寫的 code 才能拿到我們要用的 dependency。 +...

September 20, 2021

2021 iThome 鐵人賽 Day 4:Deserialization

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 4 篇,你可到 iThome 查看原文。 +文章目錄 +JSON serialization/deserialization 應該是不少 Android app 都會做的事,基本上近乎每個 Android project 都會用了一個或幾個這些 library,而 Android 都有好幾個選擇。除了上一篇提過的 org.json 套件之外,Gson、Moshi 和 Kotlin serialization 都是熱門的選擇。 +...

September 19, 2021

2021 iThome 鐵人賽 Day 3:Endpoint

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 3 篇,你可到 iThome 查看原文。 +文章目錄 +我們用到的 API endpoint 只有一個,就是用來取得港鐵機場快綫、東涌綫、屯馬綫及將軍澳綫最多四班即將到站列車的抵達時間。車站清單我們會直接寫死在 app 裏面。API 的使用方法可以在資料一線通網站(香港政府 open data 的網站)內找到。這個 API 是不需要額外申請 API key,只是一個普通的 HTTP GET request,然後 response body 是一個 JSON object。 +...

September 18, 2021

2021 iThome 鐵人賽 Day 2:Architecture

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 2 篇,你可到 iThome 查看原文。 +文章目錄 +Architecture Components 以前 Android Developers 網站沒有特別提及過寫 Android app 應該用甚麼 architecture,直至到近年 Google 因應社群的要求才建議使用 MVVM並推出了對應 MVVM 的 Architecture Components library。下圖是 Android Developers 網站建議的 architecture: +...

September 17, 2021

2021 iThome 鐵人賽 Day 1:Intro

本篇文章是 2021 iThome 鐵人賽參賽題目「寫一個列車抵站時間 Android App」的第 1 篇,你可到 iThome 查看原文。 +文章目錄 +早陣子(2021 年 6 月 27 日)港鐵屯馬綫全綫通車,當日有電視台訪問了一名鐵路迷,他受訪時調寄家傳戶曉的英國民謠《綠䄂子》即興唱了一句自創歌詞「屯馬開通真的很興奮」。那我們就以港鐵實時列車抵站時間做例子,示範一些現在 Android 開發流行的東西。 +App 會有兩頁:選擇車站頁和抵站時間頁。選擇車站頁就是列出某條行車綫的車站,讓用戶點選查閱該站的列車抵站時間;抵站時間頁就是顯示某行車綫及車站的列車抵站時間。 +...

September 16, 2021
+ PaperMod
\ No newline at end of file diff --git a/tags/android/page/5/index.html b/tags/android/page/5/index.html index 6fa14684..06037fc0 100644 --- a/tags/android/page/5/index.html +++ b/tags/android/page/5/index.html @@ -1,7 +1,19 @@ Android | EricLog -

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取...

November 14, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 in...

August 13, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自...

February 29, 2020

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散...

July 20, 2019

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular...

March 10, 2018
© 2024 EricLog +

文字末端 icon 排版

以前試過做一些 UI 是 TextView 旁邊有一個 icon,然後同一行最右邊會有另一個 Button。如果文字過長就加上省略號,但文字不夠長的話 icon 要緊貼那個文字而那個 Button 就固定在右邊。TextView 有 compound drawable,但如果想那個 icon 有 click listener 又用不了。所以最後都要分開 TextView 和 ImageView/ImageButton 兩個 view。 +...

April 10, 2021

Android SMS Verification APIs

SMS 驗證應該是一個在 Android app 頗為常見的需求。一般做法都是先讓用戶填寫電話號碼,然後 app 會把電話號碼交到 backend 再透過 SMS gateway 發送含有驗證碼短訊, 當用戶收到 SMS 後再把內文的驗證碼輸入到 app 中。如果想省卻用戶輪入文字的話有一些 app 會透過 READ_SMS 權限讀取 SMS 內容來抽取驗證碼,但 Google Play 已經限制非預設短訊 app 不可以有 READ_SMS 權限。 +...

March 14, 2021

Notification Channel 自訂音效

自從由 Android 8 開始,如果要顯示 notification 的話就一定要指定一個 notification channel,否則系統不會顯示。Notification channel 的目的是讓用戶能自行調節 app 的各式 notification 的提示方法,例如有沒有音效、會不會彈出 heads-up notification 之類。如果 app 想自訂 notification 的聲音亦都要經 notification channel 設定(但用戶可以之後自行變更 notification channel 的音效) +...

March 13, 2021

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法: +...

November 14, 2020

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 int、float、double 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。 +...

August 13, 2020

Moshi Kotlin Codegen + R8 出現 parameter type is null

Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。 +...

March 15, 2020

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: +java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch Crashlytics Org Id > Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811 我的 app 是在 Firebase 開了兩個 project,一個是開發時用的,另一個是 production 用的。但只有 production 才有這個錯誤。如果把 release build 設為不上載 ProGuard/R8 mapping file 的話就不會有這個錯誤。 +...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 +如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。 +...

February 29, 2020

Android App Icon 規格

一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon: +...

July 20, 2019

Linkify 自動轉換成網址

最近工作需要將不完整的網址變成網址,但輸入的 string 可以是普通的文字,亦可以是一個沒有 http:// 或 https:// 的網址,亦可以一個完整的網址。但 TLD 有太多,如果自己寫 regular expression 做檢查的話那句 regular expression 就會好長,而且要定時補上日後新推出的 TLD。 +...

March 10, 2018
+ PaperMod
\ No newline at end of file diff --git a/tags/android/page/6/index.html b/tags/android/page/6/index.html index 17861e56..3727e13c 100644 --- a/tags/android/page/6/index.html +++ b/tags/android/page/6/index.html @@ -1,10 +1,19 @@ Android | EricLog -

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boile...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserial...

September 16, 2017

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。Trai...

June 26, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門...

April 25, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 +

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。 +@Parcelize data class Product(val name: String, val price: Double) : Parcelable 留意要在 build.gradle 加上: +...

February 11, 2018

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。 +...

December 1, 2017

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。 +...

October 27, 2017

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable。 +...

September 16, 2017

TrainBoard for Android TV

之前買了一個小米盒子國際版,一直都想用它來寫一些 Android TV app。到了最近才將我第一個 Android TV app TrainBoard 上架,這個 app 亦都是我第一個用 Kotlin 寫的 Android app。TrainBoard 提供港鐵將軍澳綫、東涌綫、機場快綫、迪士尼綫及西鐵綫列車的預計到達時間,而這個 app 是為 MTR Service Update 而做的。 +...

June 26, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。 +...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。 +...

May 22, 2017

Android 隱藏 signing config

其實官方網站有介紹過做法,不過就令到 build 那時一定要有 keystore.properties,否則就不能 build。部分 CI 可能會針對 Android 會提供專門的方式來設定 release keystore 和密碼。而在 VCS checkout source code 後在 file system 補上 keystore.properties 和 keystore 未必可以在 CI 環境上做到。所以我將那個教學稍作改動,令到當 keystore.properties 不存在時就不提供 signing config,使它能 build 未加簽的 apk,然後才讓 CI 加簽 apk。 +...

April 25, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 小米盒子國際版 ...

December 19, 2016

Android Pay 登陸香港

之前有傳聞在今個月推出的 Android Pay 終於在昨日正式登陸香港。目前支援滙豐、恆生、渣打、東亞、星展及大新銀行的 Visa 及 MasterCard 信用卡,Tap & Go 都可以加入去 Android Pay。我有一張恒生 Visa 和一張中銀 MasterCard,但因為不支援中銀的關係只可以加入恒生信用卡。 除了信用卡,Android Pay 可以加入會員卡。這個功能就是用來儲存會員卡的條碼,Android Pay 是不會檢查會員卡號碼的真確性。在付款前可以在電話展示條碼。這樣就可以不用帶太多會員卡,而且還會在你經過商鋪時提醒你可以用會員卡。不過有人在 759 阿信屋試過被職員拒絶,要他一定要出示實體會員卡。 ...

October 21, 2016
+ PaperMod \ No newline at end of file diff --git a/tags/android/page/7/index.html b/tags/android/page/7/index.html index bdc467ef..b1f4d7e3 100644 --- a/tags/android/page/7/index.html +++ b/tags/android/page/7/index.html @@ -1,15 +1,19 @@ Android | EricLog -

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 +

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 ...

August 29, 2016

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 ...

August 30, 2015

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 ...

January 12, 2015

Android Studio 0.5.2 + ActionBarCompat 在 Android 2.3 設備死 app

最近開始轉用 Android Studio 寫 Android app。昨日升級到 0.5.2 時發現 gradle.xml 和另外兩個 .iml 檔案的路徑都被改為絕對路徑,而非先前的 $PROJECT_DIR$ 和 $MODULE_DIR$。Google 過似乎還未有解決方法,現在惟有不 commit 這幾個檔案。 另一個問題是在 Android 2.3 的設備運行用了 ActionBarCompat 的 app 會「彈 app」。只要你的 activity 有 action overflow 的話,當按動設備的 Menu 掣時,activity 就會自行結束。但在 logcat 就不覺有 exception 的 stack trace。Google 過之後就發現原來新版 Android Studio 所附帶的 Gradle 用了新的 PNG cruncher 會令到 Android 2.3 死機。目前的解決方法是使用舊版 PNG cruncher。 ...

March 23, 2014

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。 -...

January 12, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 -...

September 8, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯...

August 4, 2012

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011
January 12, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。 +...

December 14, 2012

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

September 8, 2012

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。 +...

August 4, 2012

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。 +...

August 10, 2011

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011
+ PaperMod \ No newline at end of file diff --git a/tags/android/page/8/index.html b/tags/android/page/8/index.html index 5962fd91..ea8a6304 100644 --- a/tags/android/page/8/index.html +++ b/tags/android/page/8/index.html @@ -1,6 +1,7 @@ Android | EricLog -

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯...

April 22, 2011
© 2024 EricLog +

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 Android 2.2 更新會收不到 Google Chrome to Phone 的 Push notification 和 Moto 輸入法的問題。 +...

April 22, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/api/index.html b/tags/api/index.html index 2bf1bac5..760a4703 100644 --- a/tags/api/index.html +++ b/tags/api/index.html @@ -1,6 +1,8 @@ API | EricLog -

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線...

April 11, 2011
© 2024 EricLog +

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。 +資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。 +...

April 11, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/api/index.xml b/tags/api/index.xml index c3268e9e..4ef0f367 100644 --- a/tags/api/index.xml +++ b/tags/api/index.xml @@ -1 +1,2 @@ -API on EricLoghttps://eric.swiftzer.net/tags/api/Recent content in API on EricLogHugo -- gohugo.iozh-twMon, 11 Apr 2011 12:38:40 +0800資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線 \ No newline at end of file +API on EricLoghttps://eric.swiftzer.net/tags/api/Recent content in API on EricLogHugo -- 0.139.2zh-twMon, 11 Apr 2011 12:38:40 +0800資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/<p>香港政府近年來積極推廣電子政府。除了推出「<a href="http://www.gov.hk/">香港政府一站通</a>」、「<a href="http://www.map.gov.hk">地理資訊地圖</a>」、「<a href="http://ptes.td.gov.hk/">公共交通查詢服務網站 (PTES)</a>」之外,最近還推出「<a href="http://www.gov.hk/tc/theme/psi/">資料一線通</a>」服務,將公共資料公開讓大眾使用。</p> +<p>資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。</p> \ No newline at end of file diff --git a/tags/chrome/index.html b/tags/chrome/index.html index fe78b027..da1197a2 100644 --- a/tags/chrome/index.html +++ b/tags/chrome/index.html @@ -1,6 +1,7 @@ Chrome | EricLog -

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe...

March 31, 2012
© 2024 EricLog +

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。 +...

March 31, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/chrome/index.xml b/tags/chrome/index.xml index 2bec94a1..7fc2b370 100644 --- a/tags/chrome/index.xml +++ b/tags/chrome/index.xml @@ -1 +1 @@ -Chrome on EricLoghttps://eric.swiftzer.net/tags/chrome/Recent content in Chrome on EricLogHugo -- gohugo.iozh-twSat, 31 Mar 2012 23:19:27 +0800下載最新版本的 Chromium for Windows 方法https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Sat, 31 Mar 2012 23:19:27 +0800https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe \ No newline at end of file +Chrome on EricLoghttps://eric.swiftzer.net/tags/chrome/Recent content in Chrome on EricLogHugo -- 0.139.2zh-twSat, 31 Mar 2012 23:19:27 +0800下載最新版本的 Chromium for Windows 方法https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Sat, 31 Mar 2012 23:19:27 +0800https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/<p><a href="http://www.chromium.org/">Chromium</a> 是 <a href="https://www.google.com/chrome">Google Chrome</a> 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 <em>chrome.exe</em> 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。</p> \ No newline at end of file diff --git a/tags/cocoapods/index.html b/tags/cocoapods/index.html index e8d8f77d..b953d235 100644 --- a/tags/cocoapods/index.html +++ b/tags/cocoapods/index.html @@ -1,7 +1,7 @@ CocoaPods | EricLog -

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 -...

September 28, 2016
© 2024 EricLog +

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 +...

September 28, 2016
+ PaperMod
\ No newline at end of file diff --git a/tags/cocoapods/index.xml b/tags/cocoapods/index.xml index 8ebf7f3a..d558faa0 100644 --- a/tags/cocoapods/index.xml +++ b/tags/cocoapods/index.xml @@ -1 +1 @@ -CocoaPods on EricLoghttps://eric.swiftzer.net/tags/cocoapods/Recent content in CocoaPods on EricLogHugo -- gohugo.iozh-twWed, 28 Sep 2016 10:53:11 +0800自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p> \ No newline at end of file +CocoaPods on EricLoghttps://eric.swiftzer.net/tags/cocoapods/Recent content in CocoaPods on EricLogHugo -- 0.139.2zh-twWed, 28 Sep 2016 10:53:11 +0800自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p> \ No newline at end of file diff --git a/tags/css/index.html b/tags/css/index.html index b8380ae8..2027a266 100644 --- a/tags/css/index.html +++ b/tags/css/index.html @@ -1,8 +1,9 @@ CSS | EricLog -

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: +

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: CSS 要 inline,<style> 並不是全部 email client 都能支援 排版要用 <table> 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 ...

March 11, 2015

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! -...

June 8, 2013

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011
June 8, 2013

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011
+ PaperMod \ No newline at end of file diff --git a/tags/css/index.xml b/tags/css/index.xml index 85b118ff..eb12406a 100644 --- a/tags/css/index.xml +++ b/tags/css/index.xml @@ -1,7 +1,7 @@ -CSS on EricLoghttps://eric.swiftzer.net/tags/css/Recent content in CSS on EricLogHugo -- gohugo.iozh-twWed, 11 Mar 2015 23:04:29 +0800Inline CSShttps://eric.swiftzer.net/2015/03/inline-css/Wed, 11 Mar 2015 23:04:29 +0800https://eric.swiftzer.net/2015/03/inline-css/<p>最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 <a href="http://zurb.com/ink/">Ink</a>,其他的可以參考 <a href="http://responsiveemailresources.com/">Responsive Email Resources</a> 網站)因為 HTML email 和平時做網頁分別很大,例如:</p> +CSS on EricLoghttps://eric.swiftzer.net/tags/css/Recent content in CSS on EricLogHugo -- 0.139.2zh-twWed, 11 Mar 2015 23:04:29 +0800Inline CSShttps://eric.swiftzer.net/2015/03/inline-css/Wed, 11 Mar 2015 23:04:29 +0800https://eric.swiftzer.net/2015/03/inline-css/<p>最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 <a href="http://zurb.com/ink/">Ink</a>,其他的可以參考 <a href="http://responsiveemailresources.com/">Responsive Email Resources</a> 網站)因為 HTML email 和平時做網頁分別很大,例如:</p> <ul> <li>CSS 要 inline,<code>&lt;style&gt;</code> 並不是全部 email client 都能支援</li> <li>排版要用 <code>&lt;table&gt;</code></li> <li>圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 <a href="http://gulpjs.com/">Gulp</a> 配 <a href="https://www.npmjs.com/package/gulp-inline-css">gulp-inline-css</a> 和 <a href="https://www.npmjs.com/package/gulp-inline">gulp-inline</a>,效果不錯。</li> -</ul>萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p>下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 \ No newline at end of file +</ul>萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p>下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/<p><a href="http://www.droidfonts.com/">Droid Sans</a> 是 <a href="http://www.android.com/">Android</a> 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:</p> \ No newline at end of file diff --git a/tags/data.one/index.html b/tags/data.one/index.html index a3edcc9d..ae0732f6 100644 --- a/tags/data.one/index.html +++ b/tags/data.one/index.html @@ -1,6 +1,7 @@ Data.One | EricLog -

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤...

September 25, 2014
© 2024 EricLog +

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。 +...

September 25, 2014
+ PaperMod
\ No newline at end of file diff --git a/tags/data.one/index.xml b/tags/data.one/index.xml index eaf8ff95..e7f0bb72 100644 --- a/tags/data.one/index.xml +++ b/tags/data.one/index.xml @@ -1 +1 @@ -Data.One on EricLoghttps://eric.swiftzer.net/tags/data.one/Recent content in Data.One on EricLogHugo -- gohugo.iozh-twThu, 25 Sep 2014 01:00:51 +0800Data.One 的行車速度圖https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/Thu, 25 Sep 2014 01:00:51 +0800https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤 \ No newline at end of file +Data.One on EricLoghttps://eric.swiftzer.net/tags/data.one/Recent content in Data.One on EricLogHugo -- 0.139.2zh-twThu, 25 Sep 2014 01:00:51 +0800Data.One 的行車速度圖https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/Thu, 25 Sep 2014 01:00:51 +0800https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/<p>香港政府的 <a href="http://www.gov.hk/tc/theme/psi/datasets/">Data.One</a> 有提供運輸署的<a href="http://www.gov.hk/tc/theme/psi/datasets/speedmap.htm">行車速度圖</a> XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。</p> \ No newline at end of file diff --git a/tags/facebook/index.html b/tags/facebook/index.html index 2c94287d..17aa0735 100644 --- a/tags/facebook/index.html +++ b/tags/facebook/index.html @@ -1,6 +1,7 @@ Facebook | EricLog -

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一...

December 14, 2012
© 2024 EricLog +

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。 +...

December 14, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/facebook/index.xml b/tags/facebook/index.xml index 22c68dd9..9e978680 100644 --- a/tags/facebook/index.xml +++ b/tags/facebook/index.xml @@ -1 +1 @@ -Facebook on EricLoghttps://eric.swiftzer.net/tags/facebook/Recent content in Facebook on EricLogHugo -- gohugo.iozh-twFri, 14 Dec 2012 13:11:57 +0800Facebook for Android 出了更新了!https://eric.swiftzer.net/2012/12/facebook-for-android-updated/Fri, 14 Dec 2012 13:11:57 +0800https://eric.swiftzer.net/2012/12/facebook-for-android-updated/早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一 \ No newline at end of file +Facebook on EricLoghttps://eric.swiftzer.net/tags/facebook/Recent content in Facebook on EricLogHugo -- 0.139.2zh-twFri, 14 Dec 2012 13:11:57 +0800Facebook for Android 出了更新了!https://eric.swiftzer.net/2012/12/facebook-for-android-updated/Fri, 14 Dec 2012 13:11:57 +0800https://eric.swiftzer.net/2012/12/facebook-for-android-updated/<p><a href="http://chinese.engadget.com/2012/09/12/zuckerberg-html-5-facebook-mobile-app-mistake/">早前</a> Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 <a href="https://play.google.com/store/apps/details?id=com.facebook.katana">Facebook Android app</a> 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來<a href="http://unwire.hk/2012/12/14/android-facebook-no-more-broken-chinese/software/">一眾香港用戶投訴</a>,指界面會轉用簡體中文,甚至連<a href="http://hk.apple.nextmedia.com/realtime/finance/20121214/51184200">《蘋果日報》</a>都有報道,指 Facebook app 出現簡體中文是要打中內地市場。</p> \ No newline at end of file diff --git a/tags/firebase/index.html b/tags/firebase/index.html index 6a84b5f6..05a787c7 100644 --- a/tags/firebase/index.html +++ b/tags/firebase/index.html @@ -1,6 +1,11 @@ Firebase | EricLog -

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉...

June 1, 2024

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自...

February 29, 2020
© 2024 EricLog +

Firebase Cloud Messaging legacy API

如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 HTTP v1 API。 +...

June 1, 2024

Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法

最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: +java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id > Could not fetch Crashlytics Org Id > Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811 我的 app 是在 Firebase 開了兩個 project,一個是開發時用的,另一個是 production 用的。但只有 production 才有這個錯誤。如果把 release build 設為不上載 ProGuard/R8 mapping file 的話就不會有這個錯誤。 +...

March 14, 2020

Firebase Cloud Messaging

最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 +如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。 +...

February 29, 2020
+ PaperMod
\ No newline at end of file diff --git a/tags/firebase/index.xml b/tags/firebase/index.xml index 88eda6b2..04fbc630 100644 --- a/tags/firebase/index.xml +++ b/tags/firebase/index.xml @@ -1 +1,7 @@ -Firebase on EricLoghttps://eric.swiftzer.net/tags/firebase/Recent content in Firebase on EricLogHugo -- gohugo.iozh-twSat, 01 Jun 2024 14:00:00 +0800Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法https://eric.swiftzer.net/2020/03/firebase-crashlyticsorgidexception/Sat, 14 Mar 2020 11:55:17 +0800https://eric.swiftzer.net/2020/03/firebase-crashlyticsorgidexception/最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤: java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id &gt; com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id &gt; Could not fetchFirebase Cloud Messaginghttps://eric.swiftzer.net/2020/02/firebase-cloud-messaging/Sat, 29 Feb 2020 14:07:18 +0800https://eric.swiftzer.net/2020/02/firebase-cloud-messaging/最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。 如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 \ No newline at end of file +Firebase on EricLoghttps://eric.swiftzer.net/tags/firebase/Recent content in Firebase on EricLogHugo -- 0.139.2zh-twSat, 01 Jun 2024 14:00:00 +0800Firebase Cloud Messaging legacy APIhttps://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/2024/06/firebase-cloud-messaging-legacy-api/<p>如果有用 Firebase Cloud Messaging (FCM) 或者其他關於 FCM 的第三方 SDK 的話應該會收到通知說 6 月 21 日會停用舊版的 FCM API(即是供 server 發送 notification 那個 API endpoint)。之後就要轉用 <a href="https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send">HTTP v1 API</a>。</p>Firebase Crashlytics 的 CrashlyticsOrgIdException 解決方法https://eric.swiftzer.net/2020/03/firebase-crashlyticsorgidexception/Sat, 14 Mar 2020 11:55:17 +0800https://eric.swiftzer.net/2020/03/firebase-crashlyticsorgidexception/<p>最近為自己的 app 加入 Firebase Crashlytics SDK beta(即是使用 Google 的 Firebase Crashlytics Gradle plugin 而不是用 Fabric 那個),但在 build release APK 時出現下面的錯誤:</p> +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">java.io.IOException: com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id +</span></span><span class="line"><span class="cl">&gt; com.google.firebase.crashlytics.buildtools.exception.CrashlyticsOrgIdException: Could not fetch Crashlytics Org Id +</span></span><span class="line"><span class="cl"> &gt; Could not fetch Crashlytics Org Id +</span></span><span class="line"><span class="cl"> &gt; Unable to fetch Crashlytics Org Id using app id 1:731121766578:android:4e799392a2e62811 +</span></span></code></pre></div><p>我的 app 是在 Firebase 開了兩個 project,一個是開發時用的,另一個是 production 用的。但只有 production 才有這個錯誤。如果把 release build 設為不上載 ProGuard/R8 mapping file 的話就不會有這個錯誤。</p>Firebase Cloud Messaginghttps://eric.swiftzer.net/2020/02/firebase-cloud-messaging/Sat, 29 Feb 2020 14:07:18 +0800https://eric.swiftzer.net/2020/02/firebase-cloud-messaging/<p>最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。</p> +<p>如果要整合到 Android 的話,需要建立一個新的 <code>Service</code> class 並繼承自 <a href="https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService"><code>FirebaseMessagingService</code></a>。這個 <code>Service</code> 有一個叫 <code>onMessageReceived</code> 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。</p> \ No newline at end of file diff --git a/tags/firefox/index.html b/tags/firefox/index.html index 3375bbe5..2072ae81 100644 --- a/tags/firefox/index.html +++ b/tags/firefox/index.html @@ -1,6 +1,7 @@ Firefox | EricLog -

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖...

May 2, 2009
© 2024 EricLog +

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。 +...

May 2, 2009
+ PaperMod
\ No newline at end of file diff --git a/tags/firefox/index.xml b/tags/firefox/index.xml index 1a2bdce0..52fded06 100644 --- a/tags/firefox/index.xml +++ b/tags/firefox/index.xml @@ -1 +1 @@ -Firefox on EricLoghttps://eric.swiftzer.net/tags/firefox/Recent content in Firefox on EricLogHugo -- gohugo.iozh-twSat, 02 May 2009 11:54:16 +0800Pencil Projecthttps://eric.swiftzer.net/2009/05/pencil-project/Sat, 02 May 2009 11:54:16 +0800https://eric.swiftzer.net/2009/05/pencil-project/在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖 \ No newline at end of file +Firefox on EricLoghttps://eric.swiftzer.net/tags/firefox/Recent content in Firefox on EricLogHugo -- 0.139.2zh-twSat, 02 May 2009 11:54:16 +0800Pencil Projecthttps://eric.swiftzer.net/2009/05/pencil-project/Sat, 02 May 2009 11:54:16 +0800https://eric.swiftzer.net/2009/05/pencil-project/<p>在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。</p> \ No newline at end of file diff --git a/tags/git/index.html b/tags/git/index.html index 0c0ef4dc..c674db92 100644 --- a/tags/git/index.html +++ b/tags/git/index.html @@ -1,7 +1,11 @@ Git | EricLog -

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話...

August 24, 2020

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 -...

August 29, 2016

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有...

December 29, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話...

May 27, 2013
© 2024 EricLog +

Conventional Commits 和 commitlint

Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。 +...

August 24, 2020

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。 +...

September 24, 2016

在 Android app 內顯示 Git commit hash

有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 app 的 build.gradle 就可以了。 +...

August 29, 2016

ConEmu

最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有分頁功能的 console,和 Mac 的 iTerm 有點相似。它的特色就是可以讓你自由地設定各項細節。由字型、字體大小到啟動時開啟那一個目錄都能設定到。它除了可以調用 Windows 的 Command Prompt (cmd.exe) 之外,還可以調用 PowerShell、Bash 等等的 shell。如果用 Bash 的話可以用 Git 提供的版本,這個版本可以支援基本的 Bash 指令,用來執行 PHP CLI 程式、Grunt、PHPUnit 都有顏色顯示。 +...

December 29, 2013

Bitbucket 免費 Academic License

Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話可以考慮用 Bitbucket。Bitbucket 最多可以與五個人共用私人的 repo,但它有推薦制度來增加共用名額,最多能與八個人共用同一個私人 repo。 +...

May 27, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/git/index.xml b/tags/git/index.xml index c6b5f579..1fcaef08 100644 --- a/tags/git/index.xml +++ b/tags/git/index.xml @@ -1 +1 @@ -Git on EricLoghttps://eric.swiftzer.net/tags/git/Recent content in Git on EricLogHugo -- gohugo.iozh-twMon, 24 Aug 2020 23:27:07 +0800Conventional Commits 和 commitlinthttps://eric.swiftzer.net/2020/08/conventional-commits-commitlint/Mon, 24 Aug 2020 23:27:07 +0800https://eric.swiftzer.net/2020/08/conventional-commits-commitlint/Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話xUniquehttps://eric.swiftzer.net/2016/09/xunique/Sat, 24 Sep 2016 10:58:43 +0800https://eric.swiftzer.net/2016/09/xunique/Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS在 Android app 內顯示 Git commit hashhttps://eric.swiftzer.net/2016/08/android-app-git-commit-hash/Mon, 29 Aug 2016 22:13:46 +0800https://eric.swiftzer.net/2016/08/android-app-git-commit-hash/<p>有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 <em>app</em> 的 <em>build.gradle</em> 就可以了。</p>ConEmuhttps://eric.swiftzer.net/2013/12/conemu/Sun, 29 Dec 2013 23:17:40 +0800https://eric.swiftzer.net/2013/12/conemu/最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。ConEmu 是一個在 Windows 上使用而且有Bitbucket 免費 Academic Licensehttps://eric.swiftzer.net/2013/05/bitbucket-free-academic-license/Mon, 27 May 2013 12:06:12 +0800https://eric.swiftzer.net/2013/05/bitbucket-free-academic-license/Bitbucket 和 GitHub 一樣都是有名的 Git repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話 \ No newline at end of file +Git on EricLoghttps://eric.swiftzer.net/tags/git/Recent content in Git on EricLogHugo -- 0.139.2zh-twMon, 24 Aug 2020 23:27:07 +0800Conventional Commits 和 commitlinthttps://eric.swiftzer.net/2020/08/conventional-commits-commitlint/Mon, 24 Aug 2020 23:27:07 +0800https://eric.swiftzer.net/2020/08/conventional-commits-commitlint/<p><a href="https://www.conventionalcommits.org/">Conventional Commits</a> 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。</p>xUniquehttps://eric.swiftzer.net/2016/09/xunique/Sat, 24 Sep 2016 10:58:43 +0800https://eric.swiftzer.net/2016/09/xunique/<p>Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。</p>在 Android app 內顯示 Git commit hashhttps://eric.swiftzer.net/2016/08/android-app-git-commit-hash/Mon, 29 Aug 2016 22:13:46 +0800https://eric.swiftzer.net/2016/08/android-app-git-commit-hash/<p>有些 Android app 除了顯示版本號碼之外,還會有該版本的 Git commit hash。如果有好幾部測試機做測試的話,可能會在開發時先後在不同的機安裝過不同版本的 app。但就沒有每次都更新版本號碼。加了 commit hash 就容易分辨 app 的實際版本。其實要顯示 commit hash 的做法不太難,不需要每次人手更改的。只需要改一下 <em>app</em> 的 <em>build.gradle</em> 就可以了。</p>ConEmuhttps://eric.swiftzer.net/2013/12/conemu/Sun, 29 Dec 2013 23:17:40 +0800https://eric.swiftzer.net/2013/12/conemu/<p>最近開始用了一些需要用 command 執行的工具(Grunt 和 PHPUnit),發現一個可以支援顏色顯示的 console 會比較方便。<a href="https://code.google.com/p/conemu-maximus5/">ConEmu</a> 是一個在 Windows 上使用而且有分頁功能的 console,和 Mac 的 iTerm 有點相似。它的特色就是可以讓你自由地設定各項細節。由字型、字體大小到啟動時開啟那一個目錄都能設定到。它除了可以調用 Windows 的 Command Prompt (cmd.exe) 之外,還可以調用 PowerShell、Bash 等等的 shell。如果用 Bash 的話可以用 Git 提供的版本,這個版本可以支援基本的 Bash 指令,用來執行 PHP CLI 程式、Grunt、PHPUnit 都有顏色顯示。</p>Bitbucket 免費 Academic Licensehttps://eric.swiftzer.net/2013/05/bitbucket-free-academic-license/Mon, 27 May 2013 12:06:12 +0800https://eric.swiftzer.net/2013/05/bitbucket-free-academic-license/<p><a href="http://bitbucket.org/">Bitbucket</a> 和 <a href="http://github.com/">GitHub</a> 一樣都是有名的 <a href="http://git-scm.com/">Git</a> repository (repo) hosting 供應商。兩者的免費版差別在於 Github 免費版只可以開到公開的 repo 而 Bitbucket 就可以開到公開和私人的 Git repo,如果不想公開源碼的話可以考慮用 Bitbucket。Bitbucket 最多可以與五個人共用私人的 repo,但它有推薦制度來增加共用名額,最多能與八個人共用同一個私人 repo。</p> \ No newline at end of file diff --git a/tags/google-io/index.html b/tags/google-io/index.html index 466fff9c..3e8db9d7 100644 --- a/tags/google-io/index.html +++ b/tags/google-io/index.html @@ -1,6 +1,7 @@ Google IO | EricLog -

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011
© 2024 EricLog +

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/google-io/index.xml b/tags/google-io/index.xml index d1d06c91..c39e19c9 100644 --- a/tags/google-io/index.xml +++ b/tags/google-io/index.xml @@ -1 +1 @@ -Google IO on EricLoghttps://eric.swiftzer.net/tags/google-io/Recent content in Google IO on EricLogHugo -- gohugo.iozh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 \ No newline at end of file +Google IO on EricLoghttps://eric.swiftzer.net/tags/google-io/Recent content in Google IO on EricLogHugo -- 0.139.2zh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/<p><a href="http://www.droidfonts.com/">Droid Sans</a> 是 <a href="http://www.android.com/">Android</a> 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:</p> \ No newline at end of file diff --git a/tags/google/index.html b/tags/google/index.html index 5610dd5c..ae5c2cff 100644 --- a/tags/google/index.html +++ b/tags/google/index.html @@ -1,11 +1,21 @@ Google | EricLog -

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 準備內容 上次我們用 2019 年香港公眾假期作例...

April 14, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script...

October 17, 2018

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 -...

September 8, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe...

March 31, 2012

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環...

March 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 由於目前有太少人可以試用 Google W...

October 24, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: +

用 Google Apps Script 發送電郵

在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 +準備內容 上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。 +...

April 14, 2019

用 Google Apps Script 建立 Google Calendar event

Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。 +...

October 17, 2018

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

September 8, 2012

下載最新版本的 Chromium for Windows 方法

Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exe 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。 +...

March 31, 2012

Google Map Street View 已經登陸香港

曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 +Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環、氹仔及澳門半島南部。拍攝出來的全景圖基本上都沒有大問題,只是有部分的模糊處理有錯。例如將巴士站牌、車身的非車牌部分都被模糊。較為有趣的相信是下圖的例子(位於天城路): +...

March 11, 2010

Google Wave 試用報告

試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 +Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave。Blip 就是一個個的訊息,亦即是在 Google Wave 對話的基本單位。一個 Wave 之中有不同的 Blip,其他成員可以就個別的 Blip 發表回應(訧像討論區的引用功能般)。那麼上一層的 Blip 就叫做 Wavelet。 +...

November 4, 2009

收到了 Google Wave Preview 的邀請函

在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 +由於目前有太少人可以試用 Google Wave,所以暫時未能提供試用心得,日後將會補上。 +...

October 24, 2009

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: Microsoft OLE DB Provider for SQL Server error ‘80040e31’ Timeout expired /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -...

April 30, 2009
April 30, 2009
+ PaperMod \ No newline at end of file diff --git a/tags/google/index.xml b/tags/google/index.xml index acddffa5..a4219b8e 100644 --- a/tags/google/index.xml +++ b/tags/google/index.xml @@ -1,4 +1,11 @@ -Google on EricLoghttps://eric.swiftzer.net/tags/google/Recent content in Google on EricLogHugo -- gohugo.iozh-twSun, 14 Apr 2019 15:08:36 +0800用 Google Apps Script 發送電郵https://eric.swiftzer.net/2019/04/google-apps-script-send-email/Sun, 14 Apr 2019 15:08:36 +0800https://eric.swiftzer.net/2019/04/google-apps-script-send-email/在上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。 準備內容 上次我們用 2019 年香港公眾假期作例用 Google Apps Script 建立 Google Calendar eventhttps://eric.swiftzer.net/2018/10/google-apps-script-create-calendar-event/Wed, 17 Oct 2018 14:49:46 +0800https://eric.swiftzer.net/2018/10/google-apps-script-create-calendar-event/Google Apps Script 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps ScriptSony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p>下載最新版本的 Chromium for Windows 方法https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Sat, 31 Mar 2012 23:19:27 +0800https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Chromium 是 Google Chrome 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 chrome.exeGoogle Map Street View 已經登陸香港https://eric.swiftzer.net/2010/03/google-map-street-view-for-hk/Thu, 11 Mar 2010 19:11:16 +0800https://eric.swiftzer.net/2010/03/google-map-street-view-for-hk/曾經在 2009 年 2 月 12 日看到 Google Map Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。 Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環Google Wave 試用報告https://eric.swiftzer.net/2009/11/google-wave-review/Wed, 04 Nov 2009 17:03:58 +0800https://eric.swiftzer.net/2009/11/google-wave-review/試用了 Google Wave 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。 Wave、Wavelet 和 Blip Google 將 Google Wave 入面的討論串命名為 Wave收到了 Google Wave Preview 的邀請函https://eric.swiftzer.net/2009/10/google-wave-preview-invitations/Sat, 24 Oct 2009 10:49:45 +0800https://eric.swiftzer.net/2009/10/google-wave-preview-invitations/在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 I-Circle 申請。 由於目前有太少人可以試用 Google W公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> +Google on EricLoghttps://eric.swiftzer.net/tags/google/Recent content in Google on EricLogHugo -- 0.139.2zh-twSun, 14 Apr 2019 15:08:36 +0800用 Google Apps Script 發送電郵https://eric.swiftzer.net/2019/04/google-apps-script-send-email/Sun, 14 Apr 2019 15:08:36 +0800https://eric.swiftzer.net/2019/04/google-apps-script-send-email/<p>在<a href="https://eric.swiftzer.net/2018/10/google-apps-script-create-calendar-event/">上一篇</a>為大家介紹了如何用 <a href="https://developers.google.com/apps-script/">Google Apps Script</a> 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。</p> +<!-- more --> +<h2 id="準備內容">準備內容</h2> +<p>上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。</p>用 Google Apps Script 建立 Google Calendar eventhttps://eric.swiftzer.net/2018/10/google-apps-script-create-calendar-event/Wed, 17 Oct 2018 14:49:46 +0800https://eric.swiftzer.net/2018/10/google-apps-script-create-calendar-event/<p><a href="https://developers.google.com/apps-script/">Google Apps Script</a> 是一套以 JavaScript 造的 API,可以讓你寫程式控制 Google Apps 內 Docs、Spreadsheet、Gmail、Drive 等等的功能。這次介紹如何用 Google Apps Script 建立 Google Calendar 的 event。我們會把部分建立 event 時所需要的資料放入 spreadsheet 內(會以 2019 年香港公眾假期作例子),然後用 Google Apps Script 讀取 spreadsheet 的內容再建立 event,效果就像 mail merge 般。</p>Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p>下載最新版本的 Chromium for Windows 方法https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/Sat, 31 Mar 2012 23:19:27 +0800https://eric.swiftzer.net/2012/03/get-the-latest-snapshot-of-chromium-for-windows/<p><a href="http://www.chromium.org/">Chromium</a> 是 <a href="https://www.google.com/chrome">Google Chrome</a> 的開源版,界面和用法與 Google Chrome 幾乎一樣。在最新版的 Chromium 中可能窺看到新版 Google Chrome 的功能。不過 Chromium 最大的特色其實是它不用安裝,只需解壓縮 ZIP 檔,雙擊 <em>chrome.exe</em> 就可以開到 Chromium。最適合放進 USB 手指中,這樣就可以在公用電腦(如學校)中用到 Chromium 了。</p>Google Map Street View 已經登陸香港https://eric.swiftzer.net/2010/03/google-map-street-view-for-hk/Thu, 11 Mar 2010 19:11:16 +0800https://eric.swiftzer.net/2010/03/google-map-street-view-for-hk/<p>曾經在 <a href="http://bbs.i-circle.net/viewtopic.php?f=12&amp;t=9121">2009 年 2 月 12 日</a>看到 <a href="http://maps.google.com.hk">Google Map</a> Street View 的拍攝車在天壇街出沒,今天 Google Map Street View 已經登陸香港和澳門了。</p> +<p>Street View 在香港已經涵蓋了大部分的道路,而澳門就主要涵蓋路環、氹仔及澳門半島南部。拍攝出來的全景圖基本上都沒有大問題,只是有部分的模糊處理有錯。例如將巴士站牌、車身的非車牌部分都被模糊。較為有趣的相信是下圖的例子(位於<a href="http://maps.google.com.hk/?ie=UTF8&amp;hq=&amp;hnear=%E4%B9%9D%E9%BE%8D&amp;ll=22.45974,114.002895&amp;spn=0,359.895372&amp;t=h&amp;z=14&amp;brcurrent=3,0x3403f08807915f99:0xd64729f174fcb284,1,0x3403f00a63639461:0xba0588d41c361655&amp;layer=c&amp;cbll=22.459622,114.002961&amp;panoid=i0wiVCnu5ebv6QIp9b6ZiA&amp;cbp=12,106.85,,1,10.32" title="Google Map 顯示">天城路</a>):</p>Google Wave 試用報告https://eric.swiftzer.net/2009/11/google-wave-review/Wed, 04 Nov 2009 17:03:58 +0800https://eric.swiftzer.net/2009/11/google-wave-review/<p>試用了 <a href="http://wave.google.com/">Google Wave</a> 都有一個多星期。暫時 Wave 都沒有重大功能推出,現在應該可以寫試用報告了。</p> +<h3 id="wavewavelet-和-blip">Wave、Wavelet 和 Blip</h3> +<p>Google 將 Google Wave 入面的討論串命名為 Wave。Blip 就是一個個的訊息,亦即是在 Google Wave 對話的基本單位。一個 Wave 之中有不同的 Blip,其他成員可以就個別的 Blip 發表回應(訧像討論區的引用功能般)。那麼上一層的 Blip 就叫做 Wavelet。</p>收到了 Google Wave Preview 的邀請函https://eric.swiftzer.net/2009/10/google-wave-preview-invitations/Sat, 24 Oct 2009 10:49:45 +0800https://eric.swiftzer.net/2009/10/google-wave-preview-invitations/<p>在 Google Wave 剛公佈的時候,不經意地在官網申請試用。估不到現在就收到官方的邀請函。如果想要 Google Wave 的邀請函的話,請到 <a href="http://bbs.i-circle.net/viewtopic.php?f=13&amp;t=10798">I-Circle</a> 申請。</p> +<p>由於目前有太少人可以試用 Google Wave,所以暫時未能提供試用心得,日後將會補上。</p>公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> <blockquote> <p>Microsoft OLE DB Provider for SQL Server error &lsquo;80040e31&rsquo;</p> <p>Timeout expired</p> diff --git a/tags/gradle/index.html b/tags/gradle/index.html index d5e7e636..17755061 100644 --- a/tags/gradle/index.html +++ b/tags/gradle/index.html @@ -1,6 +1,7 @@ Gradle | EricLog -

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更...

October 27, 2017
© 2024 EricLog +

Android Studio 3 的 Gradle 更新

昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。 +...

October 27, 2017
+ PaperMod
\ No newline at end of file diff --git a/tags/gradle/index.xml b/tags/gradle/index.xml index ce8b1bf8..63e3113f 100644 --- a/tags/gradle/index.xml +++ b/tags/gradle/index.xml @@ -1 +1 @@ -Gradle on EricLoghttps://eric.swiftzer.net/tags/gradle/Recent content in Gradle on EricLogHugo -- gohugo.iozh-twFri, 27 Oct 2017 23:05:53 +0800Android Studio 3 的 Gradle 更新https://eric.swiftzer.net/2017/10/android-studio-3-gradle/Fri, 27 Oct 2017 23:05:53 +0800https://eric.swiftzer.net/2017/10/android-studio-3-gradle/昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更 \ No newline at end of file +Gradle on EricLoghttps://eric.swiftzer.net/tags/gradle/Recent content in Gradle on EricLogHugo -- 0.139.2zh-twFri, 27 Oct 2017 23:05:53 +0800Android Studio 3 的 Gradle 更新https://eric.swiftzer.net/2017/10/android-studio-3-gradle/Fri, 27 Oct 2017 23:05:53 +0800https://eric.swiftzer.net/2017/10/android-studio-3-gradle/<p>昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。</p> \ No newline at end of file diff --git a/tags/gulp/index.html b/tags/gulp/index.html index 51ad1bdc..cee04bfe 100644 --- a/tags/gulp/index.html +++ b/tags/gulp/index.html @@ -1,7 +1,7 @@ Gulp | EricLog -

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: -CSS 要 inline,<style> 並不是全部 email client 都能支援 排版要用 <table> 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 ...

March 11, 2015
© 2024 EricLog +

Inline CSS

最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 Ink,其他的可以參考 Responsive Email Resources 網站)因為 HTML email 和平時做網頁分別很大,例如: +CSS 要 inline,<style> 並不是全部 email client 都能支援 排版要用 <table> 圖片視乎需要當成附件,Base64 未必能被 email client 支援 但設計 email 時如果要寫 inline style 是非常難寫,所以都是用工具轉。我試過用 Gulp 配 gulp-inline-css 和 gulp-inline,效果不錯。 ...

March 11, 2015
+ PaperMod
\ No newline at end of file diff --git a/tags/gulp/index.xml b/tags/gulp/index.xml index 6a0222ee..1ecd8601 100644 --- a/tags/gulp/index.xml +++ b/tags/gulp/index.xml @@ -1,4 +1,4 @@ -Gulp on EricLoghttps://eric.swiftzer.net/tags/gulp/Recent content in Gulp on EricLogHugo -- gohugo.iozh-twWed, 11 Mar 2015 23:04:29 +0800Inline CSShttps://eric.swiftzer.net/2015/03/inline-css/Wed, 11 Mar 2015 23:04:29 +0800https://eric.swiftzer.net/2015/03/inline-css/<p>最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 <a href="http://zurb.com/ink/">Ink</a>,其他的可以參考 <a href="http://responsiveemailresources.com/">Responsive Email Resources</a> 網站)因為 HTML email 和平時做網頁分別很大,例如:</p> +Gulp on EricLoghttps://eric.swiftzer.net/tags/gulp/Recent content in Gulp on EricLogHugo -- 0.139.2zh-twWed, 11 Mar 2015 23:04:29 +0800Inline CSShttps://eric.swiftzer.net/2015/03/inline-css/Wed, 11 Mar 2015 23:04:29 +0800https://eric.swiftzer.net/2015/03/inline-css/<p>最近需要寫一個發送 email 的 program。如果要用 HTML 的話,最好都是找到現成的 HTML email framework。(例如 Zurb 的 <a href="http://zurb.com/ink/">Ink</a>,其他的可以參考 <a href="http://responsiveemailresources.com/">Responsive Email Resources</a> 網站)因為 HTML email 和平時做網頁分別很大,例如:</p> <ul> <li>CSS 要 inline,<code>&lt;style&gt;</code> 並不是全部 email client 都能支援</li> <li>排版要用 <code>&lt;table&gt;</code></li> diff --git a/tags/hexo/index.html b/tags/hexo/index.html index 9ea36e17..e4d8ee83 100644 --- a/tags/hexo/index.html +++ b/tags/hexo/index.html @@ -1,6 +1,8 @@ Hexo | EricLog -

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 Hexo 是一個用 Node 寫的 static site generator...

March 21, 2016
© 2024 EricLog +

轉用 Hexo

之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 +Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code> 之類的 tag,用了不少時間才寫完。 +...

March 21, 2016
+ PaperMod
\ No newline at end of file diff --git a/tags/hexo/index.xml b/tags/hexo/index.xml index 417d3a65..4188b6e5 100644 --- a/tags/hexo/index.xml +++ b/tags/hexo/index.xml @@ -1 +1,2 @@ -Hexo on EricLoghttps://eric.swiftzer.net/tags/hexo/Recent content in Hexo on EricLogHugo -- gohugo.iozh-twMon, 21 Mar 2016 00:17:40 +0800轉用 Hexohttps://eric.swiftzer.net/2016/03/migrated-to-hexo/Mon, 21 Mar 2016 00:17:40 +0800https://eric.swiftzer.net/2016/03/migrated-to-hexo/之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。 Hexo 是一個用 Node 寫的 static site generator \ No newline at end of file +Hexo on EricLoghttps://eric.swiftzer.net/tags/hexo/Recent content in Hexo on EricLogHugo -- 0.139.2zh-twMon, 21 Mar 2016 00:17:40 +0800轉用 Hexohttps://eric.swiftzer.net/2016/03/migrated-to-hexo/Mon, 21 Mar 2016 00:17:40 +0800https://eric.swiftzer.net/2016/03/migrated-to-hexo/<p>之前一直都想用 static site generator 來取代沿用的 WordPress。現在終於由 WordPress 轉到 Hexo,並且將網站改用 GitHub Pages 架站。</p> +<p>Hexo 是一個用 Node 寫的 static site generator,它的定位是用來造 blog,不過用來做一些簡單的文檔網站都可以。近年開始流行如 Jekyll 之類的 static site generator,主要的特色除了是可以將網站放到普通的 web hosting 外,就是可以用 Markdown 寫文章。用 Markdown 的好處就是語法比 HTML 簡單,尤其適合寫一些文字為主,不需要特別排版的文章。還有就是寫一些夾雜着程式碼的文章。Static site generator 通常都會提供 syntax highlight 功能,而且還會將生成的 syntax highlight HTML 直接匯出,無須在 client side 做 syntax highlight。之前在 WordPress 寫夾雜着不少程式碼的文章時就要不時切換 HTML 和 WYSIWYG editor 來補加 <code>&lt;code&gt;</code> 之類的 tag,用了不少時間才寫完。</p> \ No newline at end of file diff --git a/tags/hkpl/index.html b/tags/hkpl/index.html index f4e0ca26..4d34aa70 100644 --- a/tags/hkpl/index.html +++ b/tags/hkpl/index.html @@ -1,6 +1,7 @@ HKPL | EricLog -

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋...

January 4, 2012
© 2024 EricLog +

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 AddThis 按鈕,實在令人難以相信這個是政府做的網站(就算是 gov.hk 都無這個做得出色)。 +...

January 4, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/hkpl/index.xml b/tags/hkpl/index.xml index 8c4275ba..ac2a73e1 100644 --- a/tags/hkpl/index.xml +++ b/tags/hkpl/index.xml @@ -1 +1 @@ -HKPL on EricLoghttps://eric.swiftzer.net/tags/hkpl/Recent content in HKPL on EricLogHugo -- gohugo.iozh-twWed, 04 Jan 2012 21:30:41 +0800體驗全新公共圖書館系統https://eric.swiftzer.net/2012/01/ngils/Wed, 04 Jan 2012 21:30:41 +0800https://eric.swiftzer.net/2012/01/ngils/香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋 \ No newline at end of file +HKPL on EricLoghttps://eric.swiftzer.net/tags/hkpl/Recent content in HKPL on EricLogHugo -- 0.139.2zh-twWed, 04 Jan 2012 21:30:41 +0800體驗全新公共圖書館系統https://eric.swiftzer.net/2012/01/ngils/Wed, 04 Jan 2012 21:30:41 +0800https://eric.swiftzer.net/2012/01/ngils/<p><a href="http://www.hkpl.gov.hk/">香港公共圖書館</a> 早前(<a href="http://www.info.gov.hk/gia/general/201112/22/P201112210315.htm">聖誕期間</a>)升級了其<a href="https://webcat.hkpl.gov.hk/">網上圖書館目錄</a>,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 <a href="http://www.addthis.com/">AddThis</a> 按鈕,實在令人難以相信這個是政府做的網站(就算是 <a href="http://www.gov.hk/">gov.hk</a> 都無這個做得出色)。</p> \ No newline at end of file diff --git a/tags/i18n/index.html b/tags/i18n/index.html index 949e45e7..a163f281 100644 --- a/tags/i18n/index.html +++ b/tags/i18n/index.html @@ -1,6 +1,8 @@ I18n | EricLog -

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁...

June 6, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一...

December 14, 2012
© 2024 EricLog +

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。 +...

June 6, 2013

Facebook for Android 出了更新了!

早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一眾香港用戶投訴,指界面會轉用簡體中文,甚至連《蘋果日報》都有報道,指 Facebook app 出現簡體中文是要打中內地市場。 +...

December 14, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/i18n/index.xml b/tags/i18n/index.xml index 0fbe0994..25803fc5 100644 --- a/tags/i18n/index.xml +++ b/tags/i18n/index.xml @@ -1 +1 @@ -I18n on EricLoghttps://eric.swiftzer.net/tags/i18n/Recent content in I18n on EricLogHugo -- gohugo.iozh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁Facebook for Android 出了更新了!https://eric.swiftzer.net/2012/12/facebook-for-android-updated/Fri, 14 Dec 2012 13:11:57 +0800https://eric.swiftzer.net/2012/12/facebook-for-android-updated/早前 Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 Facebook Android app 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來一 \ No newline at end of file +I18n on EricLoghttps://eric.swiftzer.net/tags/i18n/Recent content in I18n on EricLogHugo -- 0.139.2zh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/<p>Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 <a href="https://github.com/caouecs/Laravel4-lang">repository</a> 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 <em>app/lang/XX/validation.php</em> 內的 <code>attributes</code> array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 <code>attributes</code> array 的話顯示錯誤訊息時會中英夾雜。</p>Facebook for Android 出了更新了!https://eric.swiftzer.net/2012/12/facebook-for-android-updated/Fri, 14 Dec 2012 13:11:57 +0800https://eric.swiftzer.net/2012/12/facebook-for-android-updated/<p><a href="http://chinese.engadget.com/2012/09/12/zuckerberg-html-5-facebook-mobile-app-mistake/">早前</a> Facebook 創辦人暨現任 CEO Mark Zuckerberg 於訪談中說到以 HTML5 編寫 <a href="https://play.google.com/store/apps/details?id=com.facebook.katana">Facebook Android app</a> 是公司決策上最大的錯誤,今日他們終於不再用 WebView + PHP 顯示貼文了!速度的確快了不少。不過郤引來<a href="http://unwire.hk/2012/12/14/android-facebook-no-more-broken-chinese/software/">一眾香港用戶投訴</a>,指界面會轉用簡體中文,甚至連<a href="http://hk.apple.nextmedia.com/realtime/finance/20121214/51184200">《蘋果日報》</a>都有報道,指 Facebook app 出現簡體中文是要打中內地市場。</p> \ No newline at end of file diff --git a/tags/index.html b/tags/index.html index 580f6d8c..5d4220f0 100644 --- a/tags/index.html +++ b/tags/index.html @@ -1,6 +1,6 @@ Tags | EricLog -
\ No newline at end of file diff --git a/tags/index.xml b/tags/index.xml index 9c55bc96..34f7fbab 100644 --- a/tags/index.xml +++ b/tags/index.xml @@ -1 +1 @@ -Tags on EricLoghttps://eric.swiftzer.net/tags/Recent content in Tags on EricLogHugo -- gohugo.iozh-twSun, 18 Aug 2024 22:00:00 +0800Androidhttps://eric.swiftzer.net/tags/android/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/tags/android/Firebasehttps://eric.swiftzer.net/tags/firebase/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/tags/firebase/2021 IThome 鐵人賽https://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Fri, 15 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Kotlinhttps://eric.swiftzer.net/tags/kotlin/Sat, 14 Nov 2020 18:52:34 +0800https://eric.swiftzer.net/tags/kotlin/Githttps://eric.swiftzer.net/tags/git/Mon, 24 Aug 2020 23:27:07 +0800https://eric.swiftzer.net/tags/git/Javahttps://eric.swiftzer.net/tags/java/Thu, 13 Aug 2020 23:50:59 +0800https://eric.swiftzer.net/tags/java/Googlehttps://eric.swiftzer.net/tags/google/Sun, 14 Apr 2019 15:08:36 +0800https://eric.swiftzer.net/tags/google/Openrefinehttps://eric.swiftzer.net/tags/openrefine/Wed, 20 Feb 2019 23:53:05 +0800https://eric.swiftzer.net/tags/openrefine/八達通https://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/信用卡https://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/ \ No newline at end of file +Tags on EricLoghttps://eric.swiftzer.net/tags/Recent content in Tags on EricLogHugo -- 0.139.2zh-twSun, 18 Aug 2024 22:00:00 +0800Androidhttps://eric.swiftzer.net/tags/android/Sun, 18 Aug 2024 22:00:00 +0800https://eric.swiftzer.net/tags/android/Firebasehttps://eric.swiftzer.net/tags/firebase/Sat, 01 Jun 2024 14:00:00 +0800https://eric.swiftzer.net/tags/firebase/2021 IThome 鐵人賽https://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Fri, 15 Oct 2021 00:00:00 +0800https://eric.swiftzer.net/tags/2021-ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD/Kotlinhttps://eric.swiftzer.net/tags/kotlin/Sat, 14 Nov 2020 18:52:34 +0800https://eric.swiftzer.net/tags/kotlin/Githttps://eric.swiftzer.net/tags/git/Mon, 24 Aug 2020 23:27:07 +0800https://eric.swiftzer.net/tags/git/Javahttps://eric.swiftzer.net/tags/java/Thu, 13 Aug 2020 23:50:59 +0800https://eric.swiftzer.net/tags/java/Googlehttps://eric.swiftzer.net/tags/google/Sun, 14 Apr 2019 15:08:36 +0800https://eric.swiftzer.net/tags/google/Openrefinehttps://eric.swiftzer.net/tags/openrefine/Wed, 20 Feb 2019 23:53:05 +0800https://eric.swiftzer.net/tags/openrefine/八達通https://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/信用卡https://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/ \ No newline at end of file diff --git a/tags/ios/index.html b/tags/ios/index.html index a53c7831..94dc6dd6 100644 --- a/tags/ios/index.html +++ b/tags/ios/index.html @@ -1,7 +1,10 @@ IOS | EricLog -

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖...

December 7, 2016

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 -...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS...

September 24, 2016
© 2024 EricLog +

nodetree 和 jazzy

這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 +nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 nodetree 指令就能印出目前目錄的結構。 +...

December 7, 2016

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 +...

September 28, 2016

xUnique

Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。 +...

September 24, 2016
+ PaperMod
\ No newline at end of file diff --git a/tags/ios/index.xml b/tags/ios/index.xml index d4e797fd..12ce0a25 100644 --- a/tags/ios/index.xml +++ b/tags/ios/index.xml @@ -1 +1,2 @@ -IOS on EricLoghttps://eric.swiftzer.net/tags/ios/Recent content in IOS on EricLogHugo -- gohugo.iozh-twWed, 07 Dec 2016 23:27:14 +0800nodetree 和 jazzyhttps://eric.swiftzer.net/2016/12/nodetree-jazzy/Wed, 07 Dec 2016 23:27:14 +0800https://eric.swiftzer.net/2016/12/nodetree-jazzy/這次想介紹兩個 documentation 相關的工具﹔nodetree 和 jazzy。 nodetree 是用來生成 ASCII 樹狀目錄結構圖。先前一篇有關 iOS 的文章就是用 nodetree 生成相關的樹狀目錄結構圖自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p>xUniquehttps://eric.swiftzer.net/2016/09/xunique/Sat, 24 Sep 2016 10:58:43 +0800https://eric.swiftzer.net/2016/09/xunique/Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS \ No newline at end of file +IOS on EricLoghttps://eric.swiftzer.net/tags/ios/Recent content in IOS on EricLogHugo -- 0.139.2zh-twWed, 07 Dec 2016 23:27:14 +0800nodetree 和 jazzyhttps://eric.swiftzer.net/2016/12/nodetree-jazzy/Wed, 07 Dec 2016 23:27:14 +0800https://eric.swiftzer.net/2016/12/nodetree-jazzy/<p>這次想介紹兩個 documentation 相關的工具﹔<a href="https://www.npmjs.com/package/nodetree">nodetree</a> 和 <a href="https://github.com/realm/jazzy">jazzy</a>。</p> +<p>nodetree 是用來生成 ASCII 樹狀目錄結構圖。<a href="https://eric.swiftzer.net/2016/09/cocoapods-framework-pod">先前一篇有關 iOS 的文章</a>就是用 nodetree 生成相關的樹狀目錄結構圖。它的用法非常簡單,只需要輸入 <code>nodetree</code> 指令就能印出目前目錄的結構。</p>自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p>xUniquehttps://eric.swiftzer.net/2016/09/xunique/Sat, 24 Sep 2016 10:58:43 +0800https://eric.swiftzer.net/2016/09/xunique/<p>Xcode project 有個特色就是 VCS unfriendly。例子有 project.pbxproj 會有自動產生的 hash 和開啟 xib、storyboard 時不論有沒有修改過內容都好 Xcode 還是自動會將 macOS 和 Xcode 版本寫入。如果是 xib、storyboard 的版本問題就不難解決,因為比較難出現 conflict。但 project.pbxproj 就很容易 conflict。這是因為 Xcode project 和 Java 之類的 project 不同,Xcode 是可以讓 user 自訂 project 檔案排序,還有就是可以讓 user 決定那些檔案是屬於 project 的一部分,不是純綷只定義 file system 的某幾個資料夾就是 project 的一部分。然而這個特色卻容易出現 merge conflict。只要你和另一個組員在 Xcode file tree 同時將檔案排列次序改變,那就會在 merge 時出現 conflict。尤其是在前期開發時因為不時要開新檔案,會令 conflict 更容易出現。而解決 conflict 又特別麻煩,因為 project.pbxproj 的設計是讓 Xcode 讀,不是讓人去閱讀。</p> \ No newline at end of file diff --git a/tags/java/index.html b/tags/java/index.html index c28c32dc..747919c4 100644 --- a/tags/java/index.html +++ b/tags/java/index.html @@ -1,6 +1,9 @@ Java | EricLog -

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 in...

August 13, 2020

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類...

May 22, 2017
© 2024 EricLog +

Java 量度單位 (JSR 363 Units of Measurement API)

在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 int、float、double 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis。如果處理時間的話,現成的有 TimeUnit。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。 +...

August 13, 2020

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。 +...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。 +...

May 22, 2017
+ PaperMod
\ No newline at end of file diff --git a/tags/java/index.xml b/tags/java/index.xml index f4702a42..ede1b100 100644 --- a/tags/java/index.xml +++ b/tags/java/index.xml @@ -1 +1 @@ -Java on EricLoghttps://eric.swiftzer.net/tags/java/Recent content in Java on EricLogHugo -- gohugo.iozh-twThu, 13 Aug 2020 23:50:59 +0800Java 量度單位 (JSR 363 Units of Measurement API)https://eric.swiftzer.net/2020/08/javax-measure/Thu, 13 Aug 2020 23:50:59 +0800https://eric.swiftzer.net/2020/08/javax-measure/在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 inKotlin for Androidhttps://eric.swiftzer.net/2017/06/kotlin-for-android/Mon, 12 Jun 2017 23:43:16 +0800https://eric.swiftzer.net/2017/06/kotlin-for-android/在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教Timber live template for Java/Kotlinhttps://eric.swiftzer.net/2017/05/timber-live-template/Mon, 22 May 2017 22:53:34 +0800https://eric.swiftzer.net/2017/05/timber-live-template/最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類 \ No newline at end of file +Java on EricLoghttps://eric.swiftzer.net/tags/java/Recent content in Java on EricLogHugo -- 0.139.2zh-twThu, 13 Aug 2020 23:50:59 +0800Java 量度單位 (JSR 363 Units of Measurement API)https://eric.swiftzer.net/2020/08/javax-measure/Thu, 13 Aug 2020 23:50:59 +0800https://eric.swiftzer.net/2020/08/javax-measure/<p>在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 <code>int</code>、<code>float</code>、<code>double</code> 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 <code>millis</code> 之類的後綴。知名例子有 <code>System.currentTimeMillis</code>。如果處理時間的話,現成的有 <a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/TimeUnit.html"><code>TimeUnit</code></a>。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。</p>Kotlin for Androidhttps://eric.swiftzer.net/2017/06/kotlin-for-android/Mon, 12 Jun 2017 23:43:16 +0800https://eric.swiftzer.net/2017/06/kotlin-for-android/<p>在四月開始轉用 <a href="https://kotlinlang.org/">Kotlin</a> 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。</p>Timber live template for Java/Kotlinhttps://eric.swiftzer.net/2017/05/timber-live-template/Mon, 22 May 2017 22:53:34 +0800https://eric.swiftzer.net/2017/05/timber-live-template/<p>最近轉了用 <a href="https://kotlinlang.org/">Kotlin</a> 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat <code>logd</code>、<code>logm</code> 之類的 <a href="https://www.jetbrains.com/help/idea/using-live-templates.html">Live template</a>。<a href="https://github.com/Kotlin/anko">Anko</a> 的 <code>AnkoLogger</code> 因為用了 <a href="https://developer.android.com/reference/android/util/Log.html#isLoggable(java.lang.String,%20int)"><code>Log.isLoggable</code></a> 來包住 <code>Log.d</code> 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 <a href="https://github.com/JakeWharton/timber">Timber</a> 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。</p> \ No newline at end of file diff --git a/tags/javascript/index.html b/tags/javascript/index.html index 257742da..b842e306 100644 --- a/tags/javascript/index.html +++ b/tags/javascript/index.html @@ -1,6 +1,8 @@ JavaScript | EricLog -

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能...

February 4, 2017

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是...

September 21, 2015
© 2024 EricLog +

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。 +...

February 4, 2017

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。 +...

September 21, 2015
+ PaperMod
\ No newline at end of file diff --git a/tags/javascript/index.xml b/tags/javascript/index.xml index ceb8e3b8..d3fd2072 100644 --- a/tags/javascript/index.xml +++ b/tags/javascript/index.xml @@ -1 +1 @@ -JavaScript on EricLoghttps://eric.swiftzer.net/tags/javascript/Recent content in JavaScript on EricLogHugo -- gohugo.iozh-twSat, 04 Feb 2017 11:16:49 +0800小米藍牙手柄https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/Sat, 04 Feb 2017 11:16:49 +0800https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能在 Windows 刪除路徑名太長的檔案https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/Mon, 21 Sep 2015 23:20:51 +0800https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是 \ No newline at end of file +JavaScript on EricLoghttps://eric.swiftzer.net/tags/javascript/Recent content in JavaScript on EricLogHugo -- 0.139.2zh-twSat, 04 Feb 2017 11:16:49 +0800小米藍牙手柄https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/Sat, 04 Feb 2017 11:16:49 +0800https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/<p>之前提及過只需要在 Windows 安裝 <a href="https://github.com/irungentoo/Xiaomi_gamepad">mapper</a> 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。</p>在 Windows 刪除路徑名太長的檔案https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/Mon, 21 Sep 2015 23:20:51 +0800https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/<p>通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。</p> \ No newline at end of file diff --git a/tags/kotlin/index.html b/tags/kotlin/index.html index 26566888..f45bed58 100644 --- a/tags/kotlin/index.html +++ b/tags/kotlin/index.html @@ -1,8 +1,16 @@ Kotlin | EricLog -

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取...

November 14, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在...

April 27, 2020

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boile...

February 11, 2018

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserial...

September 16, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、min...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類...

May 22, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 +

Jetpack DataStore 搭配 kotlinx.serialization Protobuf

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法: +...

November 14, 2020

Kotlin Annotation Processor

如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。 +...

April 27, 2020

Kotlin Parcelize

Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boilerplate。 +@Parcelize data class Product(val name: String, val price: Double) : Parcelable 留意要在 build.gradle 加上: +...

February 11, 2018

Parcelable & Intent extra

Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialization。Parcelable 有點像 Java 本身的 Serializable,不過 Parcelable 是 Android SDK 內專為 Android 而特設的,所以會快過 Serializable。 +...

September 16, 2017

SemVer

剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 equals 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。 +...

June 16, 2017

Kotlin for Android

在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。 +...

June 12, 2017

Timber live template for Java/Kotlin

最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 Timber 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。 +...

May 22, 2017

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。 -...

April 22, 2017
April 22, 2017
+ PaperMod \ No newline at end of file diff --git a/tags/kotlin/index.xml b/tags/kotlin/index.xml index b144d996..81e04937 100644 --- a/tags/kotlin/index.xml +++ b/tags/kotlin/index.xml @@ -1,2 +1,5 @@ -Kotlin on EricLoghttps://eric.swiftzer.net/tags/kotlin/Recent content in Kotlin on EricLogHugo -- gohugo.iozh-twSat, 14 Nov 2020 18:52:34 +0800Jetpack DataStore 搭配 kotlinx.serialization Protobufhttps://eric.swiftzer.net/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/Sat, 14 Nov 2020 18:52:34 +0800https://eric.swiftzer.net/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取Kotlin Annotation Processorhttps://eric.swiftzer.net/2020/04/kotlin-annotation-processor/Mon, 27 Apr 2020 23:34:08 +0800https://eric.swiftzer.net/2020/04/kotlin-annotation-processor/如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在Kotlin Parcelizehttps://eric.swiftzer.net/2018/02/parcelize/Sun, 11 Feb 2018 23:28:05 +0800https://eric.swiftzer.net/2018/02/parcelize/Kotlin Android extensions 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 data class 加上 @Parcelize annotation 和 implement Parcelable interface 就能夠在 compile 時自動生成所需的 boileParcelable & Intent extrahttps://eric.swiftzer.net/2017/09/parcelable-intent-extra/Sat, 16 Sep 2017 12:03:49 +0800https://eric.swiftzer.net/2017/09/parcelable-intent-extra/Android 如果想將自己寫的 data type 的 object 傳到其他 Activity、Fragment 之類的地方的話,就要用 Parcelable 來做 serialization/deserialSemVerhttps://eric.swiftzer.net/2017/06/semver/Fri, 16 Jun 2017 00:59:11 +0800https://eric.swiftzer.net/2017/06/semver/剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 Semantic Versioning (SemVer) 的 Kotlin data class。這個 class 有 implement Comparable,是參照 SemVer 規範要比對 major、minKotlin for Androidhttps://eric.swiftzer.net/2017/06/kotlin-for-android/Mon, 12 Jun 2017 23:43:16 +0800https://eric.swiftzer.net/2017/06/kotlin-for-android/在四月開始轉用 Kotlin 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教Timber live template for Java/Kotlinhttps://eric.swiftzer.net/2017/05/timber-live-template/Mon, 22 May 2017 22:53:34 +0800https://eric.swiftzer.net/2017/05/timber-live-template/最近轉了用 Kotlin 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat logd、logm 之類的 Live template。Anko 的 AnkoLogger 因為用了 Log.isLoggable 來包住 Log.d 之類Spekhttps://eric.swiftzer.net/2017/04/spek/Sat, 22 Apr 2017 23:25:52 +0800https://eric.swiftzer.net/2017/04/spek/<p>之前一直都有留意 <a href="https://kotlinlang.org/">Kotlin</a> 這個程式語言在 Android app 開發的應用。最近試用 <a href="http://spekframework.org/">Spek</a> 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 <a href="http://rspec.info/">RSpec</a> 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。</p> +Kotlin on EricLoghttps://eric.swiftzer.net/tags/kotlin/Recent content in Kotlin on EricLogHugo -- 0.139.2zh-twSat, 14 Nov 2020 18:52:34 +0800Jetpack DataStore 搭配 kotlinx.serialization Protobufhttps://eric.swiftzer.net/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/Sat, 14 Nov 2020 18:52:34 +0800https://eric.swiftzer.net/2020/11/androidx-datastore-with-kotlinx-serialization-protobuf/<p>上月 <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a> <a href="https://blog.jetbrains.com/kotlin/2020/10/kotlinx-serialization-1-0-released/">出了 1.0 版</a>。除了支援 JSON 之外,還有支援 <a href="https://developers.google.com/protocol-buffers">Protocol Buffers (Protobuf)</a>,而且還是跨平台支援。而在前一個月 Android 出了 <a href="https://developer.android.com/topic/libraries/architecture/datastore">Jetpack DataStore</a>,它是一個用來取代 <code>SharedPreferences</code> 的 library。它有兩種用法:</p>Kotlin Annotation Processorhttps://eric.swiftzer.net/2020/04/kotlin-annotation-processor/Mon, 27 Apr 2020 23:34:08 +0800https://eric.swiftzer.net/2020/04/kotlin-annotation-processor/<p>如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 <em>build.gradle</em> 入面要用 <code>annotationProcessor</code> 或者 <code>kapt</code> 的那些 dependency。用法大概是在 code 上加上一些 <code>@</code> 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。</p>Kotlin Parcelizehttps://eric.swiftzer.net/2018/02/parcelize/Sun, 11 Feb 2018 23:28:05 +0800https://eric.swiftzer.net/2018/02/parcelize/<p><a href="https://kotlinlang.org/docs/tutorials/android-plugin.html">Kotlin Android extensions</a> 入面有一個實驗功能:Parcelize。它是一個 annotation,只需要在 <a href="https://kotlinlang.org/docs/reference/data-classes.html">data class</a> 加上 <code>@Parcelize</code> annotation 和 implement <code>Parcelable</code> interface 就能夠在 compile 時自動生成所需的 boilerplate。</p> +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="nd">@Parcelize</span> +</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="k">class</span> <span class="nc">Product</span><span class="p">(</span><span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Double</span><span class="p">)</span> <span class="p">:</span> <span class="n">Parcelable</span></span></span></code></pre></div> +<p>留意要在 build.gradle 加上:</p>Parcelable & Intent extrahttps://eric.swiftzer.net/2017/09/parcelable-intent-extra/Sat, 16 Sep 2017 12:03:49 +0800https://eric.swiftzer.net/2017/09/parcelable-intent-extra/<p>Android 如果想將自己寫的 data type 的 object 傳到其他 <code>Activity</code>、<code>Fragment</code> 之類的地方的話,就要用 <a href="https://developer.android.com/reference/android/os/Parcelable.html"><code>Parcelable</code></a> 來做 serialization/deserialization。<code>Parcelable</code> 有點像 Java 本身的 <code>Serializable</code>,不過 <code>Parcelable</code> 是 Android SDK 內專為 Android 而特設的,所以會快過 <code>Serializable</code>。</p>SemVerhttps://eric.swiftzer.net/2017/06/semver/Fri, 16 Jun 2017 00:59:11 +0800https://eric.swiftzer.net/2017/06/semver/<p>剛剛為了方便做 force update app 功能的版本號碼比對就寫了一個 <a href="http://semver.org/">Semantic Versioning (SemVer)</a> 的 Kotlin data class。這個 class 有 implement <code>Comparable</code>,是參照 SemVer 規範要比對 major、minor、patch 和 pre-release version,但 <code>equals</code> 就會再比對 build metadata(即是 Kotlin data class 的預設做法)。</p>Kotlin for Androidhttps://eric.swiftzer.net/2017/06/kotlin-for-android/Mon, 12 Jun 2017 23:43:16 +0800https://eric.swiftzer.net/2017/06/kotlin-for-android/<p>在四月開始轉用 <a href="https://kotlinlang.org/">Kotlin</a> 來寫自己的 Android app。其實上年八月左右已經留意到 Kotlin 這個 JVM 語言能在 Android app 開發時使用,不過那時因為沒有太多時間所以只是看了少許官方教學和一些外國網誌就作罷,沒有真正拿來寫 Android app。到了最近看到愈來愈多人開始轉用 Kotlin 所以才真正開始轉用。到了現在 Kotlin 更成為 Android first-class support language。</p>Timber live template for Java/Kotlinhttps://eric.swiftzer.net/2017/05/timber-live-template/Mon, 22 May 2017 22:53:34 +0800https://eric.swiftzer.net/2017/05/timber-live-template/<p>最近轉了用 <a href="https://kotlinlang.org/">Kotlin</a> 來寫自己的 Android app,但發現 Android Studio 在 Kotlin 檔案內無法使用 Logcat <code>logd</code>、<code>logm</code> 之類的 <a href="https://www.jetbrains.com/help/idea/using-live-templates.html">Live template</a>。<a href="https://github.com/Kotlin/anko">Anko</a> 的 <code>AnkoLogger</code> 因為用了 <a href="https://developer.android.com/reference/android/util/Log.html#isLoggable(java.lang.String,%20int)"><code>Log.isLoggable</code></a> 來包住 <code>Log.d</code> 之類的 method 所以在開發時看 log 不夠方便。於是就轉了用 <a href="https://github.com/JakeWharton/timber">Timber</a> 來做 logging。但是轉了 logging library 都是沒有方便的方法來產生 log message。所以最後我參考了 Android Studio 的 log live template 來做了適用於 Java 和 Kotlin 的 Timber live template。</p>Spekhttps://eric.swiftzer.net/2017/04/spek/Sat, 22 Apr 2017 23:25:52 +0800https://eric.swiftzer.net/2017/04/spek/<p>之前一直都有留意 <a href="https://kotlinlang.org/">Kotlin</a> 這個程式語言在 Android app 開發的應用。最近試用 <a href="http://spekframework.org/">Spek</a> 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 <a href="http://rspec.info/">RSpec</a> 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。</p> <p>Spek 有提供 <a href="https://plugins.jetbrains.com/plugin/8564-spek">IntelliJ IDEA/Android Studio plugin</a>,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。</p> \ No newline at end of file diff --git a/tags/laravel/index.html b/tags/laravel/index.html index c38cc85a..f8dc9e4f 100644 --- a/tags/laravel/index.html +++ b/tags/laravel/index.html @@ -1,7 +1,8 @@ Laravel | EricLog -

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁...

June 6, 2013

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 -...

June 4, 2013
© 2024 EricLog +

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。 +...

June 6, 2013

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 +...

June 4, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/laravel/index.xml b/tags/laravel/index.xml index b9f799f5..e4ea2f13 100644 --- a/tags/laravel/index.xml +++ b/tags/laravel/index.xml @@ -1 +1 @@ -Laravel on EricLoghttps://eric.swiftzer.net/tags/laravel/Recent content in Laravel on EricLogHugo -- gohugo.iozh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁Laravel 4https://eric.swiftzer.net/2013/06/laravel-4/Tue, 04 Jun 2013 00:01:06 +0800https://eric.swiftzer.net/2013/06/laravel-4/<p>我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 <code>if</code> 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。</p> \ No newline at end of file +Laravel on EricLoghttps://eric.swiftzer.net/tags/laravel/Recent content in Laravel on EricLogHugo -- 0.139.2zh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/<p>Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 <a href="https://github.com/caouecs/Laravel4-lang">repository</a> 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 <em>app/lang/XX/validation.php</em> 內的 <code>attributes</code> array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 <code>attributes</code> array 的話顯示錯誤訊息時會中英夾雜。</p>Laravel 4https://eric.swiftzer.net/2013/06/laravel-4/Tue, 04 Jun 2013 00:01:06 +0800https://eric.swiftzer.net/2013/06/laravel-4/<p>我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 <code>if</code> 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。</p> \ No newline at end of file diff --git a/tags/lenovo/index.html b/tags/lenovo/index.html index a458dbf5..f7e37efd 100644 --- a/tags/lenovo/index.html +++ b/tags/lenovo/index.html @@ -1,7 +1,9 @@ Lenovo | EricLog -

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,...

September 2, 2014

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraN...

January 11, 2013

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 -...

September 3, 2012
© 2024 EricLog +

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。 +...

September 2, 2014

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。 +...

January 11, 2013

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 +...

September 3, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/lenovo/index.xml b/tags/lenovo/index.xml index 2ae8bd93..88ea90cb 100644 --- a/tags/lenovo/index.xml +++ b/tags/lenovo/index.xml @@ -1 +1,6 @@ -Lenovo on EricLoghttps://eric.swiftzer.net/tags/lenovo/Recent content in Lenovo on EricLogHugo -- gohugo.iozh-twTue, 02 Sep 2014 22:03:22 +0800ThinkPad 死電池https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/Tue, 02 Sep 2014 22:03:22 +0800https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,TrackPoint 使用滑鼠中鍵https://eric.swiftzer.net/2013/01/trackpoint-middle-click/Fri, 11 Jan 2013 15:21:26 +0800https://eric.swiftzer.net/2013/01/trackpoint-middle-click/TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNLenovo ThinkPad X230https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/Mon, 03 Sep 2012 13:27:50 +0800https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/<p>早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。</p> \ No newline at end of file +Lenovo on EricLoghttps://eric.swiftzer.net/tags/lenovo/Recent content in Lenovo on EricLogHugo -- 0.139.2zh-twTue, 02 Sep 2014 22:03:22 +0800ThinkPad 死電池https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/Tue, 02 Sep 2014 22:03:22 +0800https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/<p>在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。</p>TrackPoint 使用滑鼠中鍵https://eric.swiftzer.net/2013/01/trackpoint-middle-click/Fri, 11 Jan 2013 15:21:26 +0800https://eric.swiftzer.net/2013/01/trackpoint-middle-click/<figure> +<img loading="lazy" src="IMG_9000.jpg"/> <figcaption> +TrackPoint +</figcaption> +</figure> +<p>Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。</p>Lenovo ThinkPad X230https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/Mon, 03 Sep 2012 13:27:50 +0800https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/<p>早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。</p> \ No newline at end of file diff --git a/tags/less/index.html b/tags/less/index.html index ba336f08..76a96cac 100644 --- a/tags/less/index.html +++ b/tags/less/index.html @@ -1,7 +1,7 @@ LESS | EricLog -

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! -...

June 8, 2013
© 2024 EricLog +

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! +...

June 8, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/less/index.xml b/tags/less/index.xml index 0d25775b..fbcbf152 100644 --- a/tags/less/index.xml +++ b/tags/less/index.xml @@ -1 +1 @@ -LESS on EricLoghttps://eric.swiftzer.net/tags/less/Recent content in LESS on EricLogHugo -- gohugo.iozh-twSat, 08 Jun 2013 12:53:35 +0800萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p> \ No newline at end of file +LESS on EricLoghttps://eric.swiftzer.net/tags/less/Recent content in LESS on EricLogHugo -- 0.139.2zh-twSat, 08 Jun 2013 12:53:35 +0800萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p> \ No newline at end of file diff --git a/tags/milestone/index.html b/tags/milestone/index.html index 89923027..ee6e7689 100644 --- a/tags/milestone/index.html +++ b/tags/milestone/index.html @@ -1,7 +1,7 @@ Milestone | EricLog -

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 -...

September 8, 2012
© 2024 EricLog +

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

September 8, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/milestone/index.xml b/tags/milestone/index.xml index a30dd4cc..28abc925 100644 --- a/tags/milestone/index.xml +++ b/tags/milestone/index.xml @@ -1 +1 @@ -Milestone on EricLoghttps://eric.swiftzer.net/tags/milestone/Recent content in Milestone on EricLogHugo -- gohugo.iozh-twSat, 08 Sep 2012 11:59:46 +0800Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p> \ No newline at end of file +Milestone on EricLoghttps://eric.swiftzer.net/tags/milestone/Recent content in Milestone on EricLogHugo -- 0.139.2zh-twSat, 08 Sep 2012 11:59:46 +0800Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p> \ No newline at end of file diff --git a/tags/motorola/index.html b/tags/motorola/index.html index e2d48dac..7feb12d9 100644 --- a/tags/motorola/index.html +++ b/tags/motorola/index.html @@ -1,7 +1,8 @@ Motorola | EricLog -

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 -...

September 8, 2012

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯...

April 22, 2011
© 2024 EricLog +

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 +...

September 8, 2012

Moto Fail

那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 Android 2.2 更新會收不到 Google Chrome to Phone 的 Push notification 和 Moto 輸入法的問題。 +...

April 22, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/motorola/index.xml b/tags/motorola/index.xml index a9cc325b..fe64a925 100644 --- a/tags/motorola/index.xml +++ b/tags/motorola/index.xml @@ -1 +1 @@ -Motorola on EricLoghttps://eric.swiftzer.net/tags/motorola/Recent content in Motorola on EricLogHugo -- gohugo.iozh-twSat, 08 Sep 2012 11:59:46 +0800Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p>Moto Failhttps://eric.swiftzer.net/2011/04/moto-fail/Fri, 22 Apr 2011 14:30:31 +0800https://eric.swiftzer.net/2011/04/moto-fail/那時買了 Milestone 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 Moto 會免費清除顯 \ No newline at end of file +Motorola on EricLoghttps://eric.swiftzer.net/tags/motorola/Recent content in Motorola on EricLogHugo -- 0.139.2zh-twSat, 08 Sep 2012 11:59:46 +0800Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p>Moto Failhttps://eric.swiftzer.net/2011/04/moto-fail/Fri, 22 Apr 2011 14:30:31 +0800https://eric.swiftzer.net/2011/04/moto-fail/<p>那時買了 <a href="http://www.motorola.com/Consumers/HK-ZH/Consumer-Product-and-Services/Mobile+Phones/Motorola-MILESTONE-HK-ZH?localeId=133">Milestone</a> 用了個多月後就發現顯示屏入塵。本來顯示屏入塵幾乎是每部手提電話的通病,而且問題亦不算嚴重到影響使用。但聽聞有用家試過 <a href="http://www.motorola.com/Consumers/HK-ZH/Home">Moto</a> 會免費清除顯示屏內的塵,所以前天將 Milestone 拿去 Moto 去除顯示屏內的塵,順道去問問 Moto 為何用 Moto 官方出的 <a href="http://www.android.com/">Android</a> 2.2 更新會收不到 <a href="https://market.android.com/details?id=com.google.android.apps.chrometophone">Google Chrome to Phone</a> 的 Push notification 和 Moto 輸入法的問題。</p> \ No newline at end of file diff --git a/tags/mp3/index.html b/tags/mp3/index.html index 8cd30bf9..2b159f6b 100644 --- a/tags/mp3/index.html +++ b/tags/mp3/index.html @@ -1,6 +1,8 @@ MP3 | EricLog -

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2...

May 17, 2015
© 2024 EricLog +

整理 MP3 ID3 tag

之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 +如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2,而下面就只揀選 ID3v2.3 UTF-16。而在 Remove 部分則剔選 ID3v1 及 APE。這樣就應該不會再有亂碼。 +...

May 17, 2015
+ PaperMod
\ No newline at end of file diff --git a/tags/mp3/index.xml b/tags/mp3/index.xml index 1afd2e64..f363ed29 100644 --- a/tags/mp3/index.xml +++ b/tags/mp3/index.xml @@ -1 +1,2 @@ -MP3 on EricLoghttps://eric.swiftzer.net/tags/mp3/Recent content in MP3 on EricLogHugo -- gohugo.iozh-twSun, 17 May 2015 22:19:27 +0800整理 MP3 ID3 taghttps://eric.swiftzer.net/2015/05/managing-mp3-id3-tag/Sun, 17 May 2015 22:19:27 +0800https://eric.swiftzer.net/2015/05/managing-mp3-id3-tag/之前有整埋 MP3 的 ID3 tag,以下是我的小分享。 如果要整理 MP3 檔的 ID3 tag 的話,可以用 Mp3tag。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2 \ No newline at end of file +MP3 on EricLoghttps://eric.swiftzer.net/tags/mp3/Recent content in MP3 on EricLogHugo -- 0.139.2zh-twSun, 17 May 2015 22:19:27 +0800整理 MP3 ID3 taghttps://eric.swiftzer.net/2015/05/managing-mp3-id3-tag/Sun, 17 May 2015 22:19:27 +0800https://eric.swiftzer.net/2015/05/managing-mp3-id3-tag/<p>之前有整埋 MP3 的 ID3 tag,以下是我的小分享。</p> +<p>如果要整理 MP3 檔的 ID3 tag 的話,可以用 <a href="http://www.mp3tag.de/en/">Mp3tag</a>。在設定頁,揀選 Tags | Mpeg,在 Write 部分只剔選 ID3v2,而下面就只揀選 ID3v2.3 UTF-16。而在 Remove 部分則剔選 ID3v1 及 APE。這樣就應該不會再有亂碼。</p> \ No newline at end of file diff --git a/tags/nfc/index.html b/tags/nfc/index.html index e1307506..1957d23c 100644 --- a/tags/nfc/index.html +++ b/tags/nfc/index.html @@ -1,6 +1,7 @@ NFC | EricLog -

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯...

August 4, 2012
© 2024 EricLog +

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。 +...

August 4, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/nfc/index.xml b/tags/nfc/index.xml index b8c0df61..c31b7342 100644 --- a/tags/nfc/index.xml +++ b/tags/nfc/index.xml @@ -1 +1 @@ -NFC on EricLoghttps://eric.swiftzer.net/tags/nfc/Recent content in NFC on EricLogHugo -- gohugo.iozh-twSat, 04 Aug 2012 21:12:56 +0800八達通查閱易https://eric.swiftzer.net/2012/08/official-octopus-app/Sat, 04 Aug 2012 21:12:56 +0800https://eric.swiftzer.net/2012/08/official-octopus-app/千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯 \ No newline at end of file +NFC on EricLoghttps://eric.swiftzer.net/tags/nfc/Recent content in NFC on EricLogHugo -- 0.139.2zh-twSat, 04 Aug 2012 21:12:56 +0800八達通查閱易https://eric.swiftzer.net/2012/08/official-octopus-app/Sat, 04 Aug 2012 21:12:56 +0800https://eric.swiftzer.net/2012/08/official-octopus-app/<p>千呼萬喚始出來!去年<a href="https://eric.swiftzer.net/2011/08/android-octopus-app/">有人用了 Android 的 NFC功能來讀取八達通卡的餘額</a>後,八達通公司終於推出了<a href="https://play.google.com/store/apps/details?id=com.octopuscards.nfc_reader">官方的八達通 Android app</a>。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。</p> \ No newline at end of file diff --git a/tags/node.js/index.html b/tags/node.js/index.html index 15b1a6a1..6433ed8a 100644 --- a/tags/node.js/index.html +++ b/tags/node.js/index.html @@ -1,6 +1,7 @@ Node.js | EricLog -

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是...

September 21, 2015
© 2024 EricLog +

在 Windows 刪除路徑名太長的檔案

通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。 +...

September 21, 2015
+ PaperMod
\ No newline at end of file diff --git a/tags/node.js/index.xml b/tags/node.js/index.xml index b053fe1c..e9fdc296 100644 --- a/tags/node.js/index.xml +++ b/tags/node.js/index.xml @@ -1 +1 @@ -Node.js on EricLoghttps://eric.swiftzer.net/tags/node.js/Recent content in Node.js on EricLogHugo -- gohugo.iozh-twMon, 21 Sep 2015 23:20:51 +0800在 Windows 刪除路徑名太長的檔案https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/Mon, 21 Sep 2015 23:20:51 +0800https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是 \ No newline at end of file +Node.js on EricLoghttps://eric.swiftzer.net/tags/node.js/Recent content in Node.js on EricLogHugo -- 0.139.2zh-twMon, 21 Sep 2015 23:20:51 +0800在 Windows 刪除路徑名太長的檔案https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/Mon, 21 Sep 2015 23:20:51 +0800https://eric.swiftzer.net/2015/09/delete-long-path-in-windows/<p>通常寫 JavaScript project 都會用到 NPM,NPM 和其他 package manager 不同之處就是每個 package 的 dependency 都會放在其 package 內的 node_modules 資料夾,而不會將所有 dependency 放去同一個資料夾內。這個做法的好處是不同的 package 即使用了相同的 module 但不同版本都不會衝突。但壞處是當一個 module 有 dependency 時,而這些 dependency 自己本身都有 dependency 時,便會令 project 的 node_modules 資料夾內有非常多層的資料夾。如果用 Windows 的話,有可能因路徑名太長不能刪除資料夾和檔案。</p> \ No newline at end of file diff --git a/tags/oneplus/index.html b/tags/oneplus/index.html index 4958d452..486e97eb 100644 --- a/tags/oneplus/index.html +++ b/tags/oneplus/index.html @@ -1,8 +1,8 @@ OnePlus | EricLog -

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 +

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 -...

January 12, 2015
January 12, 2015
+ PaperMod \ No newline at end of file diff --git a/tags/oneplus/index.xml b/tags/oneplus/index.xml index 0a558447..424c43f8 100644 --- a/tags/oneplus/index.xml +++ b/tags/oneplus/index.xml @@ -1,2 +1,2 @@ -OnePlus on EricLoghttps://eric.swiftzer.net/tags/oneplus/Recent content in OnePlus on EricLogHugo -- gohugo.iozh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> +OnePlus on EricLoghttps://eric.swiftzer.net/tags/oneplus/Recent content in OnePlus on EricLogHugo -- 0.139.2zh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> <p>之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。</p> \ No newline at end of file diff --git a/tags/open-data/index.html b/tags/open-data/index.html index ee6e920a..424d9b51 100644 --- a/tags/open-data/index.html +++ b/tags/open-data/index.html @@ -1,6 +1,9 @@ Open Data | EricLog -

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤...

September 25, 2014

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線...

April 11, 2011
© 2024 EricLog +

Data.One 的行車速度圖

香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。 +...

September 25, 2014

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。 +資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。 +...

April 11, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/open-data/index.xml b/tags/open-data/index.xml index 4871116f..ed0b20d2 100644 --- a/tags/open-data/index.xml +++ b/tags/open-data/index.xml @@ -1 +1,2 @@ -Open Data on EricLoghttps://eric.swiftzer.net/tags/open-data/Recent content in Open Data on EricLogHugo -- gohugo.iozh-twThu, 25 Sep 2014 01:00:51 +0800Data.One 的行車速度圖https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/Thu, 25 Sep 2014 01:00:51 +0800https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/香港政府的 Data.One 有提供運輸署的行車速度圖 XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線 \ No newline at end of file +Open Data on EricLoghttps://eric.swiftzer.net/tags/open-data/Recent content in Open Data on EricLogHugo -- 0.139.2zh-twThu, 25 Sep 2014 01:00:51 +0800Data.One 的行車速度圖https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/Thu, 25 Sep 2014 01:00:51 +0800https://eric.swiftzer.net/2014/09/data-one-traffic-speed-map/<p>香港政府的 <a href="http://www.gov.hk/tc/theme/psi/datasets/">Data.One</a> 有提供運輸署的<a href="http://www.gov.hk/tc/theme/psi/datasets/speedmap.htm">行車速度圖</a> XML 資料。資料以線條為單位,表示路段的行車速度。雖然它有提供 Excel 格式的路段起點和終點座標和其他基本資料,但郤沒有提供地圖示意各路段是對應那條實際的道路。而且座標系統是使用政府測量用的 HK80 而非一般網上地圖 API 的十進制 WGS84 座標。如果想使用這些資料的話,就要先將 HK80 座標轉為 WGS84,然後將點和線畫在地圖上,再對比實際的道路才能知道各條線所代表的道路。</p>資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/<p>香港政府近年來積極推廣電子政府。除了推出「<a href="http://www.gov.hk/">香港政府一站通</a>」、「<a href="http://www.map.gov.hk">地理資訊地圖</a>」、「<a href="http://ptes.td.gov.hk/">公共交通查詢服務網站 (PTES)</a>」之外,最近還推出「<a href="http://www.gov.hk/tc/theme/psi/">資料一線通</a>」服務,將公共資料公開讓大眾使用。</p> +<p>資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。</p> \ No newline at end of file diff --git a/tags/openrefine/index.html b/tags/openrefine/index.html index 2cfe6953..d1d7ffd8 100644 --- a/tags/openrefine/index.html +++ b/tags/openrefine/index.html @@ -1,9 +1,9 @@ Openrefine | EricLog -

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 +

OpenRefine GREL 筆記

OpenRefine 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。 Text facet 功能可以批量修改相近的資料,而不用寫程式 OpenRefine 內置了一種程式語言,名為 General Refine Expression Language (GREL),和 Excel 可以用公式差不多。我們用 OpenRefine 就是透過這種語言來把資料批量轉換成自己想要的東西。 值得一提的是 OpenRefine 以前由 Google 負責維護,所以介面會有以前 Google 產品的影子。 -...

February 20, 2019
February 20, 2019
+ PaperMod \ No newline at end of file diff --git a/tags/openrefine/index.xml b/tags/openrefine/index.xml index d0fe661f..a94847ae 100644 --- a/tags/openrefine/index.xml +++ b/tags/openrefine/index.xml @@ -1,4 +1,4 @@ -Openrefine on EricLoghttps://eric.swiftzer.net/tags/openrefine/Recent content in Openrefine on EricLogHugo -- gohugo.iozh-twWed, 20 Feb 2019 23:53:05 +0800OpenRefine GREL 筆記https://eric.swiftzer.net/2019/02/openrefine-snippets/Wed, 20 Feb 2019 23:53:05 +0800https://eric.swiftzer.net/2019/02/openrefine-snippets/<p><a href="http://openrefine.org/">OpenRefine</a> 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。</p> +Openrefine on EricLoghttps://eric.swiftzer.net/tags/openrefine/Recent content in Openrefine on EricLogHugo -- 0.139.2zh-twWed, 20 Feb 2019 23:53:05 +0800OpenRefine GREL 筆記https://eric.swiftzer.net/2019/02/openrefine-snippets/Wed, 20 Feb 2019 23:53:05 +0800https://eric.swiftzer.net/2019/02/openrefine-snippets/<p><a href="http://openrefine.org/">OpenRefine</a> 是一個開源的工具,用作檢視資料、加工處理後作其他用途。簡單的例子有一堆街名,部分街名用了全寫、部分用了縮寫,想將它們全部統一用全寫。它的定位是介乎 Excel 和自己寫程式之間。有時資料用 Excel 不太方便處理,但如果自己寫程式處理又因為程式只會用一次,感覺太麻煩。OpenRefine 相信可以解決到你的需要。</p> <figure> <img loading="lazy" src="text-facet.png"/> <figcaption> Text facet 功能可以批量修改相近的資料,而不用寫程式 diff --git a/tags/outlook/index.html b/tags/outlook/index.html index 5dbc3bcb..219634c7 100644 --- a/tags/outlook/index.html +++ b/tags/outlook/index.html @@ -1,7 +1,7 @@ Outlook | EricLog -

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。 -...

January 12, 2013
© 2024 EricLog +

使用 Exchange ActiveSync 同步 Outlook.com 郵箱

微軟的 Outlook.com 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用官方的專用 app 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。 +...

January 12, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/outlook/index.xml b/tags/outlook/index.xml index fe38a409..107338f7 100644 --- a/tags/outlook/index.xml +++ b/tags/outlook/index.xml @@ -1 +1 @@ -Outlook on EricLoghttps://eric.swiftzer.net/tags/outlook/Recent content in Outlook on EricLogHugo -- gohugo.iozh-twSat, 12 Jan 2013 22:51:13 +0800使用 Exchange ActiveSync 同步 Outlook.com 郵箱https://eric.swiftzer.net/2013/01/outlook-exchange-activesync/Sat, 12 Jan 2013 22:51:13 +0800https://eric.swiftzer.net/2013/01/outlook-exchange-activesync/<p>微軟的 <a href="http://outlook.com">Outlook.com</a> 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用<a href="https://play.google.com/store/apps/details?id=com.outlook.Z7">官方的專用 app</a> 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。</p> \ No newline at end of file +Outlook on EricLoghttps://eric.swiftzer.net/tags/outlook/Recent content in Outlook on EricLogHugo -- 0.139.2zh-twSat, 12 Jan 2013 22:51:13 +0800使用 Exchange ActiveSync 同步 Outlook.com 郵箱https://eric.swiftzer.net/2013/01/outlook-exchange-activesync/Sat, 12 Jan 2013 22:51:13 +0800https://eric.swiftzer.net/2013/01/outlook-exchange-activesync/<p>微軟的 <a href="http://outlook.com">Outlook.com</a> 已經開放了一段時間,如果想在 Android 上收發 Outlook.com 的電郵除了用傳統的 SMTP+POP 或者使用<a href="https://play.google.com/store/apps/details?id=com.outlook.Z7">官方的專用 app</a> 之外,其實還可以用 Exchange ActiveSync。雖然 Outlook.com 沒有交待過 Exchange ActiveSync 的設定方法,但做法其實不難,只需要用手機內置的電郵 app(不是 Gmail app)再使用本文提供的設置就可以了(方法同樣適用於 Hotmail 郵箱)。</p> \ No newline at end of file diff --git a/tags/parse/index.html b/tags/parse/index.html index a654302e..dbf1fc48 100644 --- a/tags/parse/index.html +++ b/tags/parse/index.html @@ -1,7 +1,7 @@ Parse | EricLog -

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 -...

August 30, 2015
© 2024 EricLog +

Parse + Android 收 Push Notification

Parse 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。 +...

August 30, 2015
+ PaperMod
\ No newline at end of file diff --git a/tags/parse/index.xml b/tags/parse/index.xml index 5b0a8afb..522a3250 100644 --- a/tags/parse/index.xml +++ b/tags/parse/index.xml @@ -1 +1 @@ -Parse on EricLoghttps://eric.swiftzer.net/tags/parse/Recent content in Parse on EricLogHugo -- gohugo.iozh-twSun, 30 Aug 2015 17:13:46 +0800Parse + Android 收 Push Notificationhttps://eric.swiftzer.net/2015/08/parse-android-push-notification/Sun, 30 Aug 2015 17:13:46 +0800https://eric.swiftzer.net/2015/08/parse-android-push-notification/<p><a href="https://www.parse.com">Parse</a> 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。</p> \ No newline at end of file +Parse on EricLoghttps://eric.swiftzer.net/tags/parse/Recent content in Parse on EricLogHugo -- 0.139.2zh-twSun, 30 Aug 2015 17:13:46 +0800Parse + Android 收 Push Notificationhttps://eric.swiftzer.net/2015/08/parse-android-push-notification/Sun, 30 Aug 2015 17:13:46 +0800https://eric.swiftzer.net/2015/08/parse-android-push-notification/<p><a href="https://www.parse.com">Parse</a> 的免費 plan 包含了一百萬個接收者的 push 配額,應該足夠一般 app 使用,而且比起自設 server 發送 push notification 更加方便(不用去 Google Developers Console 開 project)。但 Parse 網站所提供的 Android 的教學有點不清楚,而且都過時。在此分享一下 Android 版的基本 setup。</p> \ No newline at end of file diff --git a/tags/photography/index.html b/tags/photography/index.html index a16ed0fb..dcef6b93 100644 --- a/tags/photography/index.html +++ b/tags/photography/index.html @@ -1,7 +1,12 @@ Photography | EricLog -

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 -...

July 8, 2016

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111...

June 27, 2010
© 2024 EricLog +

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 +以下是所需工具: +有全手動模式的相機 魚眼鏡 / 廣角鏡 三腳架 Hugin Photoshop 能修改檔案 XMP 的工具 器材 按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。 +...

July 31, 2016

Olympus E-M10

不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。 +...

July 8, 2016

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 +相片中白色的輕鐵列車就是新來港的 1117。 +Photoshop 扮移軸

June 27, 2010
+ PaperMod
\ No newline at end of file diff --git a/tags/photography/index.xml b/tags/photography/index.xml index 75e5b091..b64e8b1f 100644 --- a/tags/photography/index.xml +++ b/tags/photography/index.xml @@ -1 +1,19 @@ -Photography on EricLoghttps://eric.swiftzer.net/tags/photography/Recent content in Photography on EricLogHugo -- gohugo.iozh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以Olympus E-M10https://eric.swiftzer.net/2016/07/olympus-em10/Fri, 08 Jul 2016 16:56:12 +0800https://eric.swiftzer.net/2016/07/olympus-em10/<p>不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。</p>Photoshop 扮移軸https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/Sun, 27 Jun 2010 15:43:19 +0800https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111 \ No newline at end of file +Photography on EricLoghttps://eric.swiftzer.net/tags/photography/Recent content in Photography on EricLogHugo -- 0.139.2zh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/<p>早前 Facebook 的 <a href="https://facebook360.fb.com/360-photos/">360 Photos</a> 令 Photosphre 流行起來。沒有 360 camera 的話,可以用<a href="https://play.google.com/store/apps/details?id=com.google.android.street">手機應用程式</a>駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。</p> +<!-- more --> +<p>以下是所需工具:</p> +<ul> +<li>有全手動模式的相機</li> +<li>魚眼鏡 / 廣角鏡</li> +<li>三腳架</li> +<li>Hugin</li> +<li>Photoshop</li> +<li>能修改檔案 XMP 的工具</li> +</ul> +<h2 id="器材">器材</h2> +<p>按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。</p>Olympus E-M10https://eric.swiftzer.net/2016/07/olympus-em10/Fri, 08 Jul 2016 16:56:12 +0800https://eric.swiftzer.net/2016/07/olympus-em10/<p>不經不覺我的 Olympus E-M10 相機買了都有一年了。這一年來都影了大概有四千多張相片。最初買相機的原因主要是用來拍畢業相,而且之前用的 Canon PowerShot A640 不知是不是因為發霉的關係,拍出來的相片都好像有一層霧般灰曚曚般。所以想買一部新的來取代它。亦因為這個原因,近年來都是用手機影相為主。</p>Photoshop 扮移軸https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/Sun, 27 Jun 2010 15:43:19 +0800https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/<p>之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。</p> +<p>相片中白色的輕鐵列車就是新來港的 1117。</p> +<figure> +<img loading="lazy" src="IMG_6640.jpg"/> <figcaption> +Photoshop 扮移軸 +</figcaption> +</figure> \ No newline at end of file diff --git a/tags/photoshop/index.html b/tags/photoshop/index.html index fd67a4aa..b5eacd2f 100644 --- a/tags/photoshop/index.html +++ b/tags/photoshop/index.html @@ -1,6 +1,11 @@ Photoshop | EricLog -

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以...

July 31, 2016

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111...

June 27, 2010
© 2024 EricLog +

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 +以下是所需工具: +有全手動模式的相機 魚眼鏡 / 廣角鏡 三腳架 Hugin Photoshop 能修改檔案 XMP 的工具 器材 按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。 +...

July 31, 2016

Photoshop 扮移軸

之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 +相片中白色的輕鐵列車就是新來港的 1117。 +Photoshop 扮移軸

June 27, 2010
+ PaperMod
\ No newline at end of file diff --git a/tags/photoshop/index.xml b/tags/photoshop/index.xml index 40a76bc7..28500257 100644 --- a/tags/photoshop/index.xml +++ b/tags/photoshop/index.xml @@ -1 +1,19 @@ -Photoshop on EricLoghttps://eric.swiftzer.net/tags/photoshop/Recent content in Photoshop on EricLogHugo -- gohugo.iozh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以Photoshop 扮移軸https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/Sun, 27 Jun 2010 15:43:19 +0800https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。 相片中白色的輕鐵列車就是新來港的 111 \ No newline at end of file +Photoshop on EricLoghttps://eric.swiftzer.net/tags/photoshop/Recent content in Photoshop on EricLogHugo -- 0.139.2zh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/<p>早前 Facebook 的 <a href="https://facebook360.fb.com/360-photos/">360 Photos</a> 令 Photosphre 流行起來。沒有 360 camera 的話,可以用<a href="https://play.google.com/store/apps/details?id=com.google.android.street">手機應用程式</a>駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。</p> +<!-- more --> +<p>以下是所需工具:</p> +<ul> +<li>有全手動模式的相機</li> +<li>魚眼鏡 / 廣角鏡</li> +<li>三腳架</li> +<li>Hugin</li> +<li>Photoshop</li> +<li>能修改檔案 XMP 的工具</li> +</ul> +<h2 id="器材">器材</h2> +<p>按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。</p>Photoshop 扮移軸https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/Sun, 27 Jun 2010 15:43:19 +0800https://eric.swiftzer.net/2010/06/photoshop-tilt-shift/<p>之前早就看過人用 Photoshop 扮移軸,但一直都沒有合適的相片來試玩這個效果。最近就剛好拍了幾張相來試,效果也不錯。</p> +<p>相片中白色的輕鐵列車就是新來港的 1117。</p> +<figure> +<img loading="lazy" src="IMG_6640.jpg"/> <figcaption> +Photoshop 扮移軸 +</figcaption> +</figure> \ No newline at end of file diff --git a/tags/photosphere/index.html b/tags/photosphere/index.html index 0eb4d23f..6001f18c 100644 --- a/tags/photosphere/index.html +++ b/tags/photosphere/index.html @@ -1,6 +1,9 @@ Photosphere | EricLog -

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以...

July 31, 2016
© 2024 EricLog +

Photosphere

早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 +以下是所需工具: +有全手動模式的相機 魚眼鏡 / 廣角鏡 三腳架 Hugin Photoshop 能修改檔案 XMP 的工具 器材 按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。 +...

July 31, 2016
+ PaperMod
\ No newline at end of file diff --git a/tags/photosphere/index.xml b/tags/photosphere/index.xml index aef1dfab..746e1635 100644 --- a/tags/photosphere/index.xml +++ b/tags/photosphere/index.xml @@ -1 +1,13 @@ -Photosphere on EricLoghttps://eric.swiftzer.net/tags/photosphere/Recent content in Photosphere on EricLogHugo -- gohugo.iozh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/早前 Facebook 的 360 Photos 令 Photosphre 流行起來。沒有 360 camera 的話,可以用手機應用程式駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。 以 \ No newline at end of file +Photosphere on EricLoghttps://eric.swiftzer.net/tags/photosphere/Recent content in Photosphere on EricLogHugo -- 0.139.2zh-twSun, 31 Jul 2016 23:30:52 +0800Photospherehttps://eric.swiftzer.net/2016/07/photosphere/Sun, 31 Jul 2016 23:30:52 +0800https://eric.swiftzer.net/2016/07/photosphere/<p>早前 Facebook 的 <a href="https://facebook360.fb.com/360-photos/">360 Photos</a> 令 Photosphre 流行起來。沒有 360 camera 的話,可以用<a href="https://play.google.com/store/apps/details?id=com.google.android.street">手機應用程式</a>駁相。不過用手機影相畫質會比較差。所以這次試試用相機拍攝 Photosphere。</p> +<!-- more --> +<p>以下是所需工具:</p> +<ul> +<li>有全手動模式的相機</li> +<li>魚眼鏡 / 廣角鏡</li> +<li>三腳架</li> +<li>Hugin</li> +<li>Photoshop</li> +<li>能修改檔案 XMP 的工具</li> +</ul> +<h2 id="器材">器材</h2> +<p>按照 Google 的指引,用 DSLR 拍攝 Photosphere 的話,是需要用魚眼鏡。但因為我沒有魚眼鏡的關係,所以唯有用 14-42mm kit 鏡(135 等效焦距 28mm)拍攝。用魚眼鏡拍攝的好處是因為視角夠廣,拍攝張數少,後期駁相都會比較方便。但由於沒有魚眼鏡的關係所以拍了 70 多張相來接駁。拍攝時要調去全手動模式 (M mode),對焦設成無限遠,ISO、光圈、快門、白平衡都要手動固定,否則接駁時可能會有明顥光暗差異。</p> \ No newline at end of file diff --git a/tags/php/index.html b/tags/php/index.html index f3a7ab27..65782aab 100644 --- a/tags/php/index.html +++ b/tags/php/index.html @@ -1,7 +1,8 @@ PHP | EricLog -

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁...

June 6, 2013

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 -...

June 4, 2013
© 2024 EricLog +

Laraval 4 中文語系翻譯

Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 app/lang/XX/validation.php 內的 attributes array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 attributes array 的話顯示錯誤訊息時會中英夾雜。 +...

June 6, 2013

Laravel 4

我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 if 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。 +...

June 4, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/php/index.xml b/tags/php/index.xml index 49e7c095..3eff700a 100644 --- a/tags/php/index.xml +++ b/tags/php/index.xml @@ -1 +1 @@ -PHP on EricLoghttps://eric.swiftzer.net/tags/php/Recent content in PHP on EricLogHugo -- gohugo.iozh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 repository 收集不同的翻譯,我自己也譯了繁Laravel 4https://eric.swiftzer.net/2013/06/laravel-4/Tue, 04 Jun 2013 00:01:06 +0800https://eric.swiftzer.net/2013/06/laravel-4/<p>我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 <code>if</code> 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。</p> \ No newline at end of file +PHP on EricLoghttps://eric.swiftzer.net/tags/php/Recent content in PHP on EricLogHugo -- 0.139.2zh-twThu, 06 Jun 2013 21:57:20 +0800Laraval 4 中文語系翻譯https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/Thu, 06 Jun 2013 21:57:20 +0800https://eric.swiftzer.net/2013/06/laraval-4-chinese-translation/<p>Laravel 4 不再附帶英文以外的 validataion error message 語系翻譯,如果要用其他語系的話,就要自己另外下載。Caouecs 已經在 GitHub 開了一個 <a href="https://github.com/caouecs/Laravel4-lang">repository</a> 收集不同的翻譯,我自己也譯了繁體中文,大家可以下載需要的翻譯。如果是打算做中文介面的話,最好也要將各欄位的中文名加到 <em>app/lang/XX/validation.php</em> 內的 <code>attributes</code> array 內。因為 Laravel 本身是用 input 的 name 來做 attribute 的名稱,所以如果不自訂 <code>attributes</code> array 的話顯示錯誤訊息時會中英夾雜。</p>Laravel 4https://eric.swiftzer.net/2013/06/laravel-4/Tue, 04 Jun 2013 00:01:06 +0800https://eric.swiftzer.net/2013/06/laravel-4/<p>我自己學習 PHP 其實都只是一年前開始,這是因為我選修了兩科關於 PHP 的科目。那時學的都是一些最基本的 PHP,製作 HTML 表單連驗證、修改、儲存到資料庫都是用同一個 PHP 檔案,內裏用幾個 <code>if</code> 來分隔開不同的部分,還會用 session 來重新填寫表單內容。現在回想起都覺得這種寫法非常嘔心,因為實在太長,而且夾雜着 PHP 和 HTML,自己寫完都不想再去看。</p> \ No newline at end of file diff --git a/tags/ptes/index.html b/tags/ptes/index.html index d13e7350..e1dfc5cd 100644 --- a/tags/ptes/index.html +++ b/tags/ptes/index.html @@ -1,10 +1,12 @@ PTES | EricLog -

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線...

April 11, 2011

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: +

資料一線通

香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線通」服務,將公共資料公開讓大眾使用。 +資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。 +...

April 11, 2011

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: Microsoft OLE DB Provider for SQL Server error ‘80040e31’ Timeout expired /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -...

April 30, 2009
April 30, 2009
+ PaperMod \ No newline at end of file diff --git a/tags/ptes/index.xml b/tags/ptes/index.xml index 32c6929a..2ae1cc2e 100644 --- a/tags/ptes/index.xml +++ b/tags/ptes/index.xml @@ -1,4 +1,5 @@ -PTES on EricLoghttps://eric.swiftzer.net/tags/ptes/Recent content in PTES on EricLogHugo -- gohugo.iozh-twMon, 11 Apr 2011 12:38:40 +0800資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/香港政府近年來積極推廣電子政府。除了推出「香港政府一站通」、「地理資訊地圖」、「公共交通查詢服務網站 (PTES)」之外,最近還推出「資料一線公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> +PTES on EricLoghttps://eric.swiftzer.net/tags/ptes/Recent content in PTES on EricLogHugo -- 0.139.2zh-twMon, 11 Apr 2011 12:38:40 +0800資料一線通https://eric.swiftzer.net/2011/04/data-one/Mon, 11 Apr 2011 12:38:40 +0800https://eric.swiftzer.net/2011/04/data-one/<p>香港政府近年來積極推廣電子政府。除了推出「<a href="http://www.gov.hk/">香港政府一站通</a>」、「<a href="http://www.map.gov.hk">地理資訊地圖</a>」、「<a href="http://ptes.td.gov.hk/">公共交通查詢服務網站 (PTES)</a>」之外,最近還推出「<a href="http://www.gov.hk/tc/theme/psi/">資料一線通</a>」服務,將公共資料公開讓大眾使用。</p> +<p>資料一線通目前提供公共設施的地理參考數據和主要道路的實時交通資料,供市民和機構免費下載使用,就算商業使用都是免費。地理參考數據就是各公共設施(學校、醫院、文娛康樂設施等)的 CSV 格式位置資料,例如經緯度、電話、地址等。而實時交通資料就有運輸署提供的行車速度圖、平均過海行車時間及特別交通消息,實時交通資料更提供 XML 格式供開發人員使用。網站還提供了開發說明和 Java 示範程式供開發人員參考。</p>公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> <blockquote> <p>Microsoft OLE DB Provider for SQL Server error &lsquo;80040e31&rsquo;</p> <p>Timeout expired</p> diff --git a/tags/railway/index.html b/tags/railway/index.html index bd89c635..4fbd8914 100644 --- a/tags/railway/index.html +++ b/tags/railway/index.html @@ -1,6 +1,7 @@ Railway | EricLog -

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一...

January 11, 2010
© 2024 EricLog +

輕鐵指南資訊光碟

最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一個路線圖程式。 +...

January 11, 2010
+ PaperMod
\ No newline at end of file diff --git a/tags/railway/index.xml b/tags/railway/index.xml index f73a9160..2d39e096 100644 --- a/tags/railway/index.xml +++ b/tags/railway/index.xml @@ -1 +1 @@ -Railway on EricLoghttps://eric.swiftzer.net/tags/railway/Recent content in Railway on EricLogHugo -- gohugo.iozh-twMon, 11 Jan 2010 14:01:49 +0800輕鐵指南資訊光碟https://eric.swiftzer.net/2010/01/light-rail-info-cd/Mon, 11 Jan 2010 14:01:49 +0800https://eric.swiftzer.net/2010/01/light-rail-info-cd/最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一 \ No newline at end of file +Railway on EricLoghttps://eric.swiftzer.net/tags/railway/Recent content in Railway on EricLogHugo -- 0.139.2zh-twMon, 11 Jan 2010 14:01:49 +0800輕鐵指南資訊光碟https://eric.swiftzer.net/2010/01/light-rail-info-cd/Mon, 11 Jan 2010 14:01:49 +0800https://eric.swiftzer.net/2010/01/light-rail-info-cd/<p>最近在光碟盒中找到了一隻陳年的輕鐵指南資訊光碟,應該是在 1999 年發行。裏面的 AutoRun 程式已經不能執行。碟內放有一些介紹資訊、一段影片、一個拼圖遊戲和一個路線圖程式。</p> \ No newline at end of file diff --git a/tags/react-native/index.html b/tags/react-native/index.html index 181e2af6..aed95549 100644 --- a/tags/react-native/index.html +++ b/tags/react-native/index.html @@ -1,6 +1,7 @@ React Native | EricLog -

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK...

December 1, 2017
© 2024 EricLog +

React Native Android Multi-window 多視窗支援

Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK 版本(24 或以上)。 +...

December 1, 2017
+ PaperMod
\ No newline at end of file diff --git a/tags/react-native/index.xml b/tags/react-native/index.xml index 458e24c7..fc7c7351 100644 --- a/tags/react-native/index.xml +++ b/tags/react-native/index.xml @@ -1 +1 @@ -React Native on EricLoghttps://eric.swiftzer.net/tags/react-native/Recent content in React Native on EricLogHugo -- gohugo.iozh-twFri, 01 Dec 2017 16:05:05 +0800React Native Android Multi-window 多視窗支援https://eric.swiftzer.net/2017/12/react-native-multi-window/Fri, 01 Dec 2017 16:05:05 +0800https://eric.swiftzer.net/2017/12/react-native-multi-window/Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 build.gradle 的 SDK \ No newline at end of file +React Native on EricLoghttps://eric.swiftzer.net/tags/react-native/Recent content in React Native on EricLogHugo -- 0.139.2zh-twFri, 01 Dec 2017 16:05:05 +0800React Native Android Multi-window 多視窗支援https://eric.swiftzer.net/2017/12/react-native-multi-window/Fri, 01 Dec 2017 16:05:05 +0800https://eric.swiftzer.net/2017/12/react-native-multi-window/<p>Android 7.0 (N) 新增一次顯示多個 app 功能 (Multi-window)。即是可以兩個 app 上下或左右並排。如果想你的 React Native app 能支援這個功能的話,首先要檢查 <em>build.gradle</em> 的 SDK 版本(24 或以上)。</p> \ No newline at end of file diff --git a/tags/reference/index.html b/tags/reference/index.html index 98616e15..147559de 100644 --- a/tags/reference/index.html +++ b/tags/reference/index.html @@ -1,6 +1,8 @@ Reference | EricLog -

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是...

August 26, 2014
© 2024 EricLog +

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是免費的。 +但如果不想安裝軟件的話,可以用 DevDocs。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。 +...

August 26, 2014
+ PaperMod
\ No newline at end of file diff --git a/tags/reference/index.xml b/tags/reference/index.xml index bc37d568..6258ffe9 100644 --- a/tags/reference/index.xml +++ b/tags/reference/index.xml @@ -1 +1,2 @@ -Reference on EricLoghttps://eric.swiftzer.net/tags/reference/Recent content in Reference on EricLogHugo -- gohugo.iozh-twTue, 26 Aug 2014 22:26:41 +0800DevDocshttps://eric.swiftzer.net/2014/08/devdocs/Tue, 26 Aug 2014 22:26:41 +0800https://eric.swiftzer.net/2014/08/devdocs/如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是 \ No newline at end of file +Reference on EricLoghttps://eric.swiftzer.net/tags/reference/Recent content in Reference on EricLogHugo -- 0.139.2zh-twTue, 26 Aug 2014 22:26:41 +0800DevDocshttps://eric.swiftzer.net/2014/08/devdocs/Tue, 26 Aug 2014 22:26:41 +0800https://eric.swiftzer.net/2014/08/devdocs/<p>如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 <a href="http://kapeli.com/dash">Dash</a>。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 <a href="http://zealdocs.org/">Zeal</a>,這個更加是免費的。</p> +<p>但如果不想安裝軟件的話,可以用 <a href="http://devdocs.io/">DevDocs</a>。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。</p> \ No newline at end of file diff --git a/tags/rfid/index.html b/tags/rfid/index.html index eddcbf54..12b19db0 100644 --- a/tags/rfid/index.html +++ b/tags/rfid/index.html @@ -1,6 +1,7 @@ RFID | EricLog -

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋...

January 4, 2012
© 2024 EricLog +

體驗全新公共圖書館系統

香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 AddThis 按鈕,實在令人難以相信這個是政府做的網站(就算是 gov.hk 都無這個做得出色)。 +...

January 4, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/rfid/index.xml b/tags/rfid/index.xml index 83f2f42f..f4546832 100644 --- a/tags/rfid/index.xml +++ b/tags/rfid/index.xml @@ -1 +1 @@ -RFID on EricLoghttps://eric.swiftzer.net/tags/rfid/Recent content in RFID on EricLogHugo -- gohugo.iozh-twWed, 04 Jan 2012 21:30:41 +0800體驗全新公共圖書館系統https://eric.swiftzer.net/2012/01/ngils/Wed, 04 Jan 2012 21:30:41 +0800https://eric.swiftzer.net/2012/01/ngils/香港公共圖書館 早前(聖誕期間)升級了其網上圖書館目錄,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋 \ No newline at end of file +RFID on EricLoghttps://eric.swiftzer.net/tags/rfid/Recent content in RFID on EricLogHugo -- 0.139.2zh-twWed, 04 Jan 2012 21:30:41 +0800體驗全新公共圖書館系統https://eric.swiftzer.net/2012/01/ngils/Wed, 04 Jan 2012 21:30:41 +0800https://eric.swiftzer.net/2012/01/ngils/<p><a href="http://www.hkpl.gov.hk/">香港公共圖書館</a> 早前(<a href="http://www.info.gov.hk/gia/general/201112/22/P201112210315.htm">聖誕期間</a>)升級了其<a href="https://webcat.hkpl.gov.hk/">網上圖書館目錄</a>,界面比起之前的版本更美觀、更好用。其實前幾個月 HKPL 已經將新界面公開讓公眾試用,但只能搜尋,不能登入。相信有用過舊版介面的人都會記得舊版的圖書館目錄是不能用瀏覽器的「上一頁」功能,而且搜尋的網址是有時限,將自己搜尋到的結果頁分享給其他人是不可能的。還有就是登入介面如果瀏覽器有為你儲存登入資料的話它會將登入密碼錯誤地填入身份證號碼一欄,每次登入時都要將身份證號碼一欄清空然後再重新輸入密碼方能登入續借系統。在新版圖書館目錄中,介面轉用清新的 Web 2.0 風格,左邊有個可以將目前搜尋結果提昇精確度的介面方便搜尋。加上設有手機版介面和網誌經常會有的 <a href="http://www.addthis.com/">AddThis</a> 按鈕,實在令人難以相信這個是政府做的網站(就算是 <a href="http://www.gov.hk/">gov.hk</a> 都無這個做得出色)。</p> \ No newline at end of file diff --git a/tags/sass/index.html b/tags/sass/index.html index b67b153c..28312b00 100644 --- a/tags/sass/index.html +++ b/tags/sass/index.html @@ -1,7 +1,7 @@ SASS | EricLog -

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! -...

June 8, 2013
© 2024 EricLog +

萬用 Preprocessor Compiler:Prepros

Prepros 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 CodeKit。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的! +...

June 8, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/sass/index.xml b/tags/sass/index.xml index e6fa9a95..b5738239 100644 --- a/tags/sass/index.xml +++ b/tags/sass/index.xml @@ -1 +1 @@ -SASS on EricLoghttps://eric.swiftzer.net/tags/sass/Recent content in SASS on EricLogHugo -- gohugo.iozh-twSat, 08 Jun 2013 12:53:35 +0800萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p> \ No newline at end of file +SASS on EricLoghttps://eric.swiftzer.net/tags/sass/Recent content in SASS on EricLogHugo -- 0.139.2zh-twSat, 08 Jun 2013 12:53:35 +0800萬用 Preprocessor Compiler:Preproshttps://eric.swiftzer.net/2013/06/prepros/Sat, 08 Jun 2013 12:53:35 +0800https://eric.swiftzer.net/2013/06/prepros/<p><a href="http://alphapixels.com/prepros/">Prepros</a> 是一個能支援多款 CSS、JavaScript 和 HTML Preprocessor 的 compiler,它支援 LESS、Sass、Scss、Stylus、Jade、Slim、CoffeeScript、Haml 和 Markdown,有些名我還是第一次聽。有了它就不用再裝 Ruby 之類的東西,因為它已經內置 compile 時所需的程式。它可以在 compile 的時候亦可以 minify code(minify 是指將檔案中不必要的空白字元、註解清除,令檔案變小,加快網頁載入的時間),亦支援近年流行的 Live Reload 功能(在檔案儲存時瀏覽器會即時重新載入有關的網頁,方便即時看到剛才修改過的效果,只需要安裝 Prepros 的 extension 和在 Prepros 開啟這個功能就可以了)。它支援的 Preprocessor 數量和功能可以媲美 Mac 的 <a href="http://incident57.com/codekit/">CodeKit</a>。不過 Prepros 不能直接 minify 最原始的 JavaScript 檔案,亦沒有優化圖片的功能,但都足夠一般的使用。重點的是 Prepros 是免費的!</p> \ No newline at end of file diff --git a/tags/simcity/index.html b/tags/simcity/index.html index 03a7e7c4..ea867d1c 100644 --- a/tags/simcity/index.html +++ b/tags/simcity/index.html @@ -1,7 +1,7 @@ SimCity | EricLog -

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 -...

March 24, 2013
© 2024 EricLog +

SimCity

期待了十年的 SimCity (2013) 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。 +...

March 24, 2013
+ PaperMod
\ No newline at end of file diff --git a/tags/simcity/index.xml b/tags/simcity/index.xml index 9d1592b7..e9c96e89 100644 --- a/tags/simcity/index.xml +++ b/tags/simcity/index.xml @@ -1 +1 @@ -SimCity on EricLoghttps://eric.swiftzer.net/tags/simcity/Recent content in SimCity on EricLogHugo -- gohugo.iozh-twSun, 24 Mar 2013 17:53:27 +0800SimCityhttps://eric.swiftzer.net/2013/03/simcity/Sun, 24 Mar 2013 17:53:27 +0800https://eric.swiftzer.net/2013/03/simcity/<p>期待了十年的 <a href="http://www.simcity.com/">SimCity (2013)</a> 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。</p> \ No newline at end of file +SimCity on EricLoghttps://eric.swiftzer.net/tags/simcity/Recent content in SimCity on EricLogHugo -- 0.139.2zh-twSun, 24 Mar 2013 17:53:27 +0800SimCityhttps://eric.swiftzer.net/2013/03/simcity/Sun, 24 Mar 2013 17:53:27 +0800https://eric.swiftzer.net/2013/03/simcity/<p>期待了十年的 <a href="http://www.simcity.com/">SimCity (2013)</a> 終於出了新版(不計算 SimCity Society 的話),但結果一推出就劣評如潮。首先就是將以往單機遊戲變成網上遊戲,雖然 EA 聲稱改為網上遊戲的原因並非因為 DRM,但原因很明顯示為了防盜版。而伺服器郤未能承受到全球大量的玩家,加上其他種種的 bug,令玩家憤怒。結果 EA 要送 Origin 遊戲給玩定消氣。</p> \ No newline at end of file diff --git a/tags/sony/index.html b/tags/sony/index.html index 9bdb5bb0..5c47bd7c 100644 --- a/tags/sony/index.html +++ b/tags/sony/index.html @@ -1,10 +1,10 @@ Sony | EricLog -

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 +

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 ...

January 12, 2015

Xperia ion 維修記

我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。 鏡頭鍍膜被刮傷,導致照片影像矇矓 ...

July 10, 2013

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 -...

September 8, 2012
September 8, 2012
+ PaperMod \ No newline at end of file diff --git a/tags/sony/index.xml b/tags/sony/index.xml index 72c11bd4..843d3e1f 100644 --- a/tags/sony/index.xml +++ b/tags/sony/index.xml @@ -1,4 +1,4 @@ -Sony on EricLoghttps://eric.swiftzer.net/tags/sony/Recent content in Sony on EricLogHugo -- gohugo.iozh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> +Sony on EricLoghttps://eric.swiftzer.net/tags/sony/Recent content in Sony on EricLogHugo -- 0.139.2zh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> <p>之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。</p>Xperia ion 維修記https://eric.swiftzer.net/2013/07/reparing-xperia-ion/Wed, 10 Jul 2013 23:51:35 +0800https://eric.swiftzer.net/2013/07/reparing-xperia-ion/<p>我的 Sony Xperia ion (LT28i) 用了接近一年,最近留意到拍出來的相片都是矇矓的,就像眼睛有散光一樣。對比起之前拍的相片的質素真是奇差無比。再留意一下鏡頭,發現原來鏡頭的鍍膜 (Coating) 刮傷了,導致到拍出來的相片有散光效果、相機不能對焦。</p> <figure> <img loading="lazy" src="IMG_9037.jpg"/> <figcaption> diff --git a/tags/spek/index.html b/tags/spek/index.html index f37ba164..e18284d7 100644 --- a/tags/spek/index.html +++ b/tags/spek/index.html @@ -1,8 +1,8 @@ Spek | EricLog -

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 +

Spek

之前一直都有留意 Kotlin 這個程式語言在 Android app 開發的應用。最近試用 Spek 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 RSpec 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。 Spek 有提供 IntelliJ IDEA/Android Studio plugin,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。 -...

April 22, 2017
April 22, 2017
+ PaperMod \ No newline at end of file diff --git a/tags/spek/index.xml b/tags/spek/index.xml index c81d952f..90244305 100644 --- a/tags/spek/index.xml +++ b/tags/spek/index.xml @@ -1,2 +1,2 @@ -Spek on EricLoghttps://eric.swiftzer.net/tags/spek/Recent content in Spek on EricLogHugo -- gohugo.iozh-twSat, 22 Apr 2017 23:25:52 +0800Spekhttps://eric.swiftzer.net/2017/04/spek/Sat, 22 Apr 2017 23:25:52 +0800https://eric.swiftzer.net/2017/04/spek/<p>之前一直都有留意 <a href="https://kotlinlang.org/">Kotlin</a> 這個程式語言在 Android app 開發的應用。最近試用 <a href="http://spekframework.org/">Spek</a> 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 <a href="http://rspec.info/">RSpec</a> 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。</p> +Spek on EricLoghttps://eric.swiftzer.net/tags/spek/Recent content in Spek on EricLogHugo -- 0.139.2zh-twSat, 22 Apr 2017 23:25:52 +0800Spekhttps://eric.swiftzer.net/2017/04/spek/Sat, 22 Apr 2017 23:25:52 +0800https://eric.swiftzer.net/2017/04/spek/<p>之前一直都有留意 <a href="https://kotlinlang.org/">Kotlin</a> 這個程式語言在 Android app 開發的應用。最近試用 <a href="http://spekframework.org/">Spek</a> 來做 Android project 的 local test。Spek 是一個用 Kotlin 寫的 testing framework,用法和 Ruby 的 <a href="http://rspec.info/">RSpec</a> 差不多。對比 Android project 預設用的 JUnit 4,Spek 的寫法會比較清楚。因為 JUnit 4 只靠 class 和method 來為 test 分類,不能 nested(JUnit 5 才支援)。Spek 就用 nested 的方式來把 test 分類,還有就是用 string 來定義 test 名,比起 JUnit 4 用 method 名較易閱讀。</p> <p>Spek 有提供 <a href="https://plugins.jetbrains.com/plugin/8564-spek">IntelliJ IDEA/Android Studio plugin</a>,而且還有 JUnit platform engine。所以在 Android project 上面使用都沒有太大問題。</p> \ No newline at end of file diff --git a/tags/thinkpad/index.html b/tags/thinkpad/index.html index 4da971dd..cad61fc0 100644 --- a/tags/thinkpad/index.html +++ b/tags/thinkpad/index.html @@ -1,7 +1,9 @@ ThinkPad | EricLog -

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,...

September 2, 2014

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraN...

January 11, 2013

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 -...

September 3, 2012
© 2024 EricLog +

ThinkPad 死電池

在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。 +...

September 2, 2014

TrackPoint 使用滑鼠中鍵

TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。 +...

January 11, 2013

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 +...

September 3, 2012
+ PaperMod
\ No newline at end of file diff --git a/tags/thinkpad/index.xml b/tags/thinkpad/index.xml index 17608806..3ae7736b 100644 --- a/tags/thinkpad/index.xml +++ b/tags/thinkpad/index.xml @@ -1 +1,6 @@ -ThinkPad on EricLoghttps://eric.swiftzer.net/tags/thinkpad/Recent content in ThinkPad on EricLogHugo -- gohugo.iozh-twTue, 02 Sep 2014 22:03:22 +0800ThinkPad 死電池https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/Tue, 02 Sep 2014 22:03:22 +0800https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,TrackPoint 使用滑鼠中鍵https://eric.swiftzer.net/2013/01/trackpoint-middle-click/Fri, 11 Jan 2013 15:21:26 +0800https://eric.swiftzer.net/2013/01/trackpoint-middle-click/TrackPoint Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNLenovo ThinkPad X230https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/Mon, 03 Sep 2012 13:27:50 +0800https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/<p>早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。</p> \ No newline at end of file +ThinkPad on EricLoghttps://eric.swiftzer.net/tags/thinkpad/Recent content in ThinkPad on EricLogHugo -- 0.139.2zh-twTue, 02 Sep 2014 22:03:22 +0800ThinkPad 死電池https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/Tue, 02 Sep 2014 22:03:22 +0800https://eric.swiftzer.net/2014/09/thinkpad-dead-battery/<p>在大學買的 ThinkPad X230 已經用了兩年(2012 年 8 月購入),但就先後壞了兩次電池。第一次壞是在 2014 年 2 月(一年半),用了 72 個 cycle 的電池突然不能充電和放電,Power Manager 顯示為 poor condition。由於電池只有一年保養,所以到 Lenovo 維修中心買新電池。但新電池未夠半年就有問題。</p>TrackPoint 使用滑鼠中鍵https://eric.swiftzer.net/2013/01/trackpoint-middle-click/Fri, 11 Jan 2013 15:21:26 +0800https://eric.swiftzer.net/2013/01/trackpoint-middle-click/<figure> +<img loading="lazy" src="IMG_9000.jpg"/> <figcaption> +TrackPoint +</figcaption> +</figure> +<p>Lenovo ThinkPad 的特色之一就是鍵盤中間有一個「中原一點紅 (TrackPoint)」,這個小紅點和鍵盤下面的左中右鍵合組成 UltraNav。UltraNav 可以令使用者在雙手不離開鍵盤的情況下使用滑鼠。不過這個中鍵只可以作頁面上下左右滾動或者直接作為滑鼠中鍵使用,不可以設為兩個功能同時存在。我自己是將它設定做頁面滾動,如果在瀏覽網頁時想開新分頁 / 關閉分頁的話就用 Touchpad 的三指按下 Multi-touch 動作來代替按下滑鼠中鍵。有時候為了使用滑鼠中鍵而要走去用 Touchpad 有時都覺得麻煩。</p>Lenovo ThinkPad X230https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/Mon, 03 Sep 2012 13:27:50 +0800https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/<p>早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。</p> \ No newline at end of file diff --git a/tags/tool/index.html b/tags/tool/index.html index 3110efde..f2dead6b 100644 --- a/tags/tool/index.html +++ b/tags/tool/index.html @@ -1,6 +1,8 @@ Tool | EricLog -

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是...

August 26, 2014
© 2024 EricLog +

DevDocs

如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是免費的。 +但如果不想安裝軟件的話,可以用 DevDocs。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。 +...

August 26, 2014
+ PaperMod
\ No newline at end of file diff --git a/tags/tool/index.xml b/tags/tool/index.xml index 74151133..eb9946b4 100644 --- a/tags/tool/index.xml +++ b/tags/tool/index.xml @@ -1 +1,2 @@ -Tool on EricLoghttps://eric.swiftzer.net/tags/tool/Recent content in Tool on EricLogHugo -- gohugo.iozh-twTue, 26 Aug 2014 22:26:41 +0800DevDocshttps://eric.swiftzer.net/2014/08/devdocs/Tue, 26 Aug 2014 22:26:41 +0800https://eric.swiftzer.net/2014/08/devdocs/如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 Dash。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 Zeal,這個更加是 \ No newline at end of file +Tool on EricLoghttps://eric.swiftzer.net/tags/tool/Recent content in Tool on EricLogHugo -- 0.139.2zh-twTue, 26 Aug 2014 22:26:41 +0800DevDocshttps://eric.swiftzer.net/2014/08/devdocs/Tue, 26 Aug 2014 22:26:41 +0800https://eric.swiftzer.net/2014/08/devdocs/<p>如果用 Mac 來寫程式的話,相信都有聽過一個 app 叫 <a href="http://kapeli.com/dash">Dash</a>。它是一個離線閱讀 API 文件的工具。如果是用 Windows 或者 Linux 的話,有一個類似的 app 叫 <a href="http://zealdocs.org/">Zeal</a>,這個更加是免費的。</p> +<p>但如果不想安裝軟件的話,可以用 <a href="http://devdocs.io/">DevDocs</a>。只需要在左下角點選「Select documentation」,選擇需要查閱的 API。之後就可以在左上角的搜尋欄搜尋 API。這些軟件的好處就是將不同的 API 文件整合在一處,無需到各個 library/framework 網站查閱。而 DevDocs 還會將不同的文件套上統一式樣,界面更加清新。</p> \ No newline at end of file diff --git a/tags/tvos/index.html b/tags/tvos/index.html index c5c0c0c5..a876dead 100644 --- a/tags/tvos/index.html +++ b/tags/tvos/index.html @@ -1,7 +1,7 @@ TvOS | EricLog -

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 -...

September 28, 2016
© 2024 EricLog +

自己造一個 CocoaPods Framework Pod

最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。 +...

September 28, 2016
+ PaperMod
\ No newline at end of file diff --git a/tags/tvos/index.xml b/tags/tvos/index.xml index 61bf2c58..c821b055 100644 --- a/tags/tvos/index.xml +++ b/tags/tvos/index.xml @@ -1 +1 @@ -TvOS on EricLoghttps://eric.swiftzer.net/tags/tvos/Recent content in TvOS on EricLogHugo -- gohugo.iozh-twWed, 28 Sep 2016 10:53:11 +0800自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p> \ No newline at end of file +TvOS on EricLoghttps://eric.swiftzer.net/tags/tvos/Recent content in TvOS on EricLogHugo -- 0.139.2zh-twWed, 28 Sep 2016 10:53:11 +0800自己造一個 CocoaPods Framework Podhttps://eric.swiftzer.net/2016/09/cocoapods-framework-pod/Wed, 28 Sep 2016 10:53:11 +0800https://eric.swiftzer.net/2016/09/cocoapods-framework-pod/<p>最近工作需要將一個 tvOS app 的某些 class 抽出來變成能被 CocoaPods 安裝的私家 close source framework 讓其他人用。我都是第一次做 framework,在網上的教學主要都是教純 Xcode 的做法和使用 CocoaPods 下載 project dependency,詳細提及如何造一個 CocoaPods Pod 就比較少人寫。所以就寫出來跟大家分享。</p> \ No newline at end of file diff --git a/tags/typography/index.html b/tags/typography/index.html index 511feff7..a2553fc8 100644 --- a/tags/typography/index.html +++ b/tags/typography/index.html @@ -1,6 +1,9 @@ Typography | EricLog -

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 在 FontStruct 繪製字元「F」 FontStruct 是一個提供網...

April 8, 2009
© 2024 EricLog +

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011

FontStruct:網上自製字型

近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 +在 FontStruct 繪製字元「F」 FontStruct 是一個提供網上自製字型的服務。只需要在 FontStruct 免費注冊一個戶口,你就可以使用它以 Adobe Flex 製作的介面來繪製字元。別以為它是很難用,其實用法好像在使用小畫家般。只需要選好預製的形狀,然後在格網上畫上去便行了,網站首頁的影片亦有展示它的使用方法。 +...

April 8, 2009
+ PaperMod
\ No newline at end of file diff --git a/tags/typography/index.xml b/tags/typography/index.xml index 0d04ebe7..117c0edb 100644 --- a/tags/typography/index.xml +++ b/tags/typography/index.xml @@ -1 +1,7 @@ -Typography on EricLoghttps://eric.swiftzer.net/tags/typography/Recent content in Typography on EricLogHugo -- gohugo.iozh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由FontStruct:網上自製字型https://eric.swiftzer.net/2009/04/fontstruct/Wed, 08 Apr 2009 16:30:30 +0800https://eric.swiftzer.net/2009/04/fontstruct/近年來,網上有不小服務供網民使用,例如 Zoho、Google 的一系列服務。這次介紹的是 FontStruct。 在 FontStruct 繪製字元「F」 FontStruct 是一個提供網 \ No newline at end of file +Typography on EricLoghttps://eric.swiftzer.net/tags/typography/Recent content in Typography on EricLogHugo -- 0.139.2zh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/<p><a href="http://www.droidfonts.com/">Droid Sans</a> 是 <a href="http://www.android.com/">Android</a> 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:</p>FontStruct:網上自製字型https://eric.swiftzer.net/2009/04/fontstruct/Wed, 08 Apr 2009 16:30:30 +0800https://eric.swiftzer.net/2009/04/fontstruct/<p>近年來,網上有不小服務供網民使用,例如 <a href="http://www.zoho.com/">Zoho</a>、<a href="http://www.google.com/">Google</a> 的一系列服務。這次介紹的是 <a href="http://fontstruct.fontshop.com/">FontStruct</a>。</p> +<figure> +<img loading="lazy" src="font-struct.png"/> <figcaption> +在 FontStruct 繪製字元「F」 +</figcaption> +</figure> +<p>FontStruct 是一個提供網上自製字型的服務。只需要在 FontStruct 免費注冊一個戶口,你就可以使用它以 <a href="http://www.adobe.com/products/flex/">Adobe Flex</a> 製作的介面來繪製字元。別以為它是很難用,其實用法好像在使用<a href="http://zh.wikipedia.org/wiki/%E7%94%BB%E5%9B%BE">小畫家</a>般。只需要選好預製的形狀,然後在格網上畫上去便行了,網站首頁的影片亦有展示它的使用方法。</p> \ No newline at end of file diff --git a/tags/ui/index.html b/tags/ui/index.html index 9c8b120a..05929907 100644 --- a/tags/ui/index.html +++ b/tags/ui/index.html @@ -1,6 +1,7 @@ UI | EricLog -

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖...

May 2, 2009
© 2024 EricLog +

Pencil Project

在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。 +...

May 2, 2009
+ PaperMod
\ No newline at end of file diff --git a/tags/ui/index.xml b/tags/ui/index.xml index e1b74d57..4ea70bf7 100644 --- a/tags/ui/index.xml +++ b/tags/ui/index.xml @@ -1 +1 @@ -UI on EricLoghttps://eric.swiftzer.net/tags/ui/Recent content in UI on EricLogHugo -- gohugo.iozh-twSat, 02 May 2009 11:54:16 +0800Pencil Projecthttps://eric.swiftzer.net/2009/05/pencil-project/Sat, 02 May 2009 11:54:16 +0800https://eric.swiftzer.net/2009/05/pencil-project/在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖 \ No newline at end of file +UI on EricLoghttps://eric.swiftzer.net/tags/ui/Recent content in UI on EricLogHugo -- 0.139.2zh-twSat, 02 May 2009 11:54:16 +0800Pencil Projecthttps://eric.swiftzer.net/2009/05/pencil-project/Sat, 02 May 2009 11:54:16 +0800https://eric.swiftzer.net/2009/05/pencil-project/<p>在設計網頁 / 軟件的使用界面時,我們有時都會用紙筆來繪畫界面,然後把草稿傳給程式編寫員來將界面實行。正所謂「畫意能達萬言 (A picture is worth a thousand words) 」,用圖像來表示都是比較方便易明。但是如果用紙筆的話,修改就不大方便。</p> \ No newline at end of file diff --git a/tags/web-font/index.html b/tags/web-font/index.html index bdf32b3a..022d3b24 100644 --- a/tags/web-font/index.html +++ b/tags/web-font/index.html @@ -1,6 +1,7 @@ Web Font | EricLog -

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由...

May 11, 2011
© 2024 EricLog +

下載 Droid Sans 字型

Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型: +...

May 11, 2011
+ PaperMod
\ No newline at end of file diff --git a/tags/web-font/index.xml b/tags/web-font/index.xml index 44206f27..9cd69566 100644 --- a/tags/web-font/index.xml +++ b/tags/web-font/index.xml @@ -1 +1 @@ -Web Font on EricLoghttps://eric.swiftzer.net/tags/web-font/Recent content in Web Font on EricLogHugo -- gohugo.iozh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/Droid Sans 是 Android 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 \ No newline at end of file +Web Font on EricLoghttps://eric.swiftzer.net/tags/web-font/Recent content in Web Font on EricLogHugo -- 0.139.2zh-twWed, 11 May 2011 21:43:20 +0800下載 Droid Sans 字型https://eric.swiftzer.net/2011/05/download-droid-sans/Wed, 11 May 2011 21:43:20 +0800https://eric.swiftzer.net/2011/05/download-droid-sans/<p><a href="http://www.droidfonts.com/">Droid Sans</a> 是 <a href="http://www.android.com/">Android</a> 使用者介面所使用的字型 ((雖然有部分手機製造商會自訂使用者介面的設計,但大部分的手機均使用 Droid Sans 作為介面字型。)),整個 Droid 字型家族是由 Ascender 的 Steve Matteson 設計。而 Droid 字型家族共分三個字型:</p> \ No newline at end of file diff --git "a/tags/\344\277\241\347\224\250\345\215\241/index.html" "b/tags/\344\277\241\347\224\250\345\215\241/index.html" index 72926e5a..d938df71 100644 --- "a/tags/\344\277\241\347\224\250\345\215\241/index.html" +++ "b/tags/\344\277\241\347\224\250\345\215\241/index.html" @@ -1,8 +1,8 @@ 信用卡 | EricLog -

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 +

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。 -...

April 16, 2018
April 16, 2018
+ PaperMod \ No newline at end of file diff --git "a/tags/\344\277\241\347\224\250\345\215\241/index.xml" "b/tags/\344\277\241\347\224\250\345\215\241/index.xml" index d06b85e1..21351b54 100644 --- "a/tags/\344\277\241\347\224\250\345\215\241/index.xml" +++ "b/tags/\344\277\241\347\224\250\345\215\241/index.xml" @@ -1,2 +1,2 @@ -信用卡 on EricLoghttps://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/Recent content in 信用卡 on EricLogHugo -- gohugo.iozh-twMon, 16 Apr 2018 21:40:53 +0800八達通新方向https://eric.swiftzer.net/2018/04/octopus-acquiring-business/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/2018/04/octopus-acquiring-business/<p>最近,八達通終於做應該做的事了:商戶可以用<a href="https://ezone.ulifestyle.com.hk/article/2040493/%E5%85%AB%E9%81%94%E9%80%9A%E6%9B%B4%E6%96%B0%E5%95%86%E7%94%A8%E7%89%88%20App%20%E5%85%8D%E7%A7%9F%E6%A9%9F%E5%82%B3%E7%B5%B1%E5%A0%B1%E6%94%A4%E8%A9%A6%E7%94%A8">八達通提供的商用版 app 經 NFC 向實體卡扣錢</a>,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。</p> +信用卡 on EricLoghttps://eric.swiftzer.net/tags/%E4%BF%A1%E7%94%A8%E5%8D%A1/Recent content in 信用卡 on EricLogHugo -- 0.139.2zh-twMon, 16 Apr 2018 21:40:53 +0800八達通新方向https://eric.swiftzer.net/2018/04/octopus-acquiring-business/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/2018/04/octopus-acquiring-business/<p>最近,八達通終於做應該做的事了:商戶可以用<a href="https://ezone.ulifestyle.com.hk/article/2040493/%E5%85%AB%E9%81%94%E9%80%9A%E6%9B%B4%E6%96%B0%E5%95%86%E7%94%A8%E7%89%88%20App%20%E5%85%8D%E7%A7%9F%E6%A9%9F%E5%82%B3%E7%B5%B1%E5%A0%B1%E6%94%A4%E8%A9%A6%E7%94%A8">八達通提供的商用版 app 經 NFC 向實體卡扣錢</a>,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。</p> <p>八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了<a href="https://www.octopus.com.hk/tc/consumer/mobile-payment/mobile-sim/index.html">八達通 SIM 卡</a>,但現在應該終止推廣了。</p> \ No newline at end of file diff --git "a/tags/\345\205\253\351\201\224\351\200\232/index.html" "b/tags/\345\205\253\351\201\224\351\200\232/index.html" index 78e6b514..e3eb70ae 100644 --- "a/tags/\345\205\253\351\201\224\351\200\232/index.html" +++ "b/tags/\345\205\253\351\201\224\351\200\232/index.html" @@ -1,8 +1,10 @@ 八達通 | EricLog -

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 +

八達通新方向

最近,八達通終於做應該做的事了:商戶可以用八達通提供的商用版 app 經 NFC 向實體卡扣錢,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。 八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了八達通 SIM 卡,但現在應該終止推廣了。 -...

April 16, 2018

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯...

August 4, 2012

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人...

August 10, 2011
April 16, 2018

八達通查閱易

千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。 +...

August 4, 2012

八達通手機 App 風波

最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。 +...

August 10, 2011
+ PaperMod \ No newline at end of file diff --git "a/tags/\345\205\253\351\201\224\351\200\232/index.xml" "b/tags/\345\205\253\351\201\224\351\200\232/index.xml" index ae42fb35..20a48221 100644 --- "a/tags/\345\205\253\351\201\224\351\200\232/index.xml" +++ "b/tags/\345\205\253\351\201\224\351\200\232/index.xml" @@ -1,2 +1,2 @@ -八達通 on EricLoghttps://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/Recent content in 八達通 on EricLogHugo -- gohugo.iozh-twMon, 16 Apr 2018 21:40:53 +0800八達通新方向https://eric.swiftzer.net/2018/04/octopus-acquiring-business/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/2018/04/octopus-acquiring-business/<p>最近,八達通終於做應該做的事了:商戶可以用<a href="https://ezone.ulifestyle.com.hk/article/2040493/%E5%85%AB%E9%81%94%E9%80%9A%E6%9B%B4%E6%96%B0%E5%95%86%E7%94%A8%E7%89%88%20App%20%E5%85%8D%E7%A7%9F%E6%A9%9F%E5%82%B3%E7%B5%B1%E5%A0%B1%E6%94%A4%E8%A9%A6%E7%94%A8">八達通提供的商用版 app 經 NFC 向實體卡扣錢</a>,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。</p> -<p>八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了<a href="https://www.octopus.com.hk/tc/consumer/mobile-payment/mobile-sim/index.html">八達通 SIM 卡</a>,但現在應該終止推廣了。</p>八達通查閱易https://eric.swiftzer.net/2012/08/official-octopus-app/Sat, 04 Aug 2012 21:12:56 +0800https://eric.swiftzer.net/2012/08/official-octopus-app/千呼萬喚始出來!去年有人用了 Android 的 NFC功能來讀取八達通卡的餘額後,八達通公司終於推出了官方的八達通 Android app。這個 Android app 最主要的功能在於它能夠顯八達通手機 App 風波https://eric.swiftzer.net/2011/08/android-octopus-app/Wed, 10 Aug 2011 23:51:33 +0800https://eric.swiftzer.net/2011/08/android-octopus-app/最近在 Android Market 有本地 Android app 開發者 studenttwok 發布了一個名為「八達通卡餘額閱讀器」的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人 \ No newline at end of file +八達通 on EricLoghttps://eric.swiftzer.net/tags/%E5%85%AB%E9%81%94%E9%80%9A/Recent content in 八達通 on EricLogHugo -- 0.139.2zh-twMon, 16 Apr 2018 21:40:53 +0800八達通新方向https://eric.swiftzer.net/2018/04/octopus-acquiring-business/Mon, 16 Apr 2018 21:40:53 +0800https://eric.swiftzer.net/2018/04/octopus-acquiring-business/<p>最近,八達通終於做應該做的事了:商戶可以用<a href="https://ezone.ulifestyle.com.hk/article/2040493/%E5%85%AB%E9%81%94%E9%80%9A%E6%9B%B4%E6%96%B0%E5%95%86%E7%94%A8%E7%89%88%20App%20%E5%85%8D%E7%A7%9F%E6%A9%9F%E5%82%B3%E7%B5%B1%E5%A0%B1%E6%94%A4%E8%A9%A6%E7%94%A8">八達通提供的商用版 app 經 NFC 向實體卡扣錢</a>,小商戶就毋須租拍卡機就能接受八達通付款,亦都毋須使用 O! ePay 的 QR code 功能。</p> +<p>八達通當初就是一張單純的儲值卡。八達通開拓流動支付應該要數到在 Android 2.3 (Gingerbread) 支援 NFC 的時候見到有其他 Android 開發者推出查閱餘額 app 就學人推出一個查閱交易記錄 app(因為卡內的交易紀錄被加密,所以其他 app 只可以查閱餘額)。其後亦推出了<a href="https://www.octopus.com.hk/tc/consumer/mobile-payment/mobile-sim/index.html">八達通 SIM 卡</a>,但現在應該終止推廣了。</p>八達通查閱易https://eric.swiftzer.net/2012/08/official-octopus-app/Sat, 04 Aug 2012 21:12:56 +0800https://eric.swiftzer.net/2012/08/official-octopus-app/<p>千呼萬喚始出來!去年<a href="https://eric.swiftzer.net/2011/08/android-octopus-app/">有人用了 Android 的 NFC功能來讀取八達通卡的餘額</a>後,八達通公司終於推出了<a href="https://play.google.com/store/apps/details?id=com.octopuscards.nfc_reader">官方的八達通 Android app</a>。這個 Android app 最主要的功能在於它能夠顯示最近十次的交易紀綠。</p>八達通手機 App 風波https://eric.swiftzer.net/2011/08/android-octopus-app/Wed, 10 Aug 2011 23:51:33 +0800https://eric.swiftzer.net/2011/08/android-octopus-app/<p>最近在 Android Market 有本地 Android app 開發者 <a href="http://hklightstudio.net/">studenttwok</a> 發布了一個名為<a href="https://market.android.com/details?id=com.hklight.octopusreader">「八達通卡餘額閱讀器」</a>的應用程式,透過設在手機上的 NFC (Near field communication) 裝置來讀取八達通卡內所記載的餘額。本來人家寫這個 App 就是方便大家不用到港鐵車站查閱餘額,現在卻反而弄到滿城風雨。</p> \ No newline at end of file diff --git "a/tags/\345\234\260\345\234\226/index.html" "b/tags/\345\234\260\345\234\226/index.html" index 6e9c8bff..943bec5e 100644 --- "a/tags/\345\234\260\345\234\226/index.html" +++ "b/tags/\345\234\260\345\234\226/index.html" @@ -1,10 +1,10 @@ 地圖 | EricLog -

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: +

公共交通查詢服務 (PTES)

運輸署在 4 月 28 日下午 3 時推出了「公共交通查詢服務 (Public Transport Enquiry Service, PTES)」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤: Microsoft OLE DB Provider for SQL Server error ‘80040e31’ Timeout expired /tc/search_text/routelist_info.asp, line 134 An error occurred on the server when processing the URL. Please contact the system administrator. 請查看地圖,把附近最合適的車站訂為出發地 / 目的地再搜尋。或把路程分段以減少轉乘次數。 -...

April 30, 2009
April 30, 2009
+ PaperMod \ No newline at end of file diff --git "a/tags/\345\234\260\345\234\226/index.xml" "b/tags/\345\234\260\345\234\226/index.xml" index 9ee7682f..65ef7340 100644 --- "a/tags/\345\234\260\345\234\226/index.xml" +++ "b/tags/\345\234\260\345\234\226/index.xml" @@ -1,4 +1,4 @@ -地圖 on EricLoghttps://eric.swiftzer.net/tags/%E5%9C%B0%E5%9C%96/Recent content in 地圖 on EricLogHugo -- gohugo.iozh-twThu, 30 Apr 2009 20:04:41 +0800公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> +地圖 on EricLoghttps://eric.swiftzer.net/tags/%E5%9C%B0%E5%9C%96/Recent content in 地圖 on EricLogHugo -- 0.139.2zh-twThu, 30 Apr 2009 20:04:41 +0800公共交通查詢服務 (PTES)https://eric.swiftzer.net/2009/04/ptes/Thu, 30 Apr 2009 20:04:41 +0800https://eric.swiftzer.net/2009/04/ptes/<p>運輸署在 4 月 28 日下午 3 時推出了「<a href="http://ptes.td.gov.hk/">公共交通查詢服務 (Public Transport Enquiry Service, PTES)</a>」網站。網站由運輸署和理工大學開發。我當日嘗試去使用這個網站,但不時出現錯誤:</p> <blockquote> <p>Microsoft OLE DB Provider for SQL Server error &lsquo;80040e31&rsquo;</p> <p>Timeout expired</p> diff --git "a/tags/\345\260\217\347\261\263/index.html" "b/tags/\345\260\217\347\261\263/index.html" index c378898c..87e3435e 100644 --- "a/tags/\345\260\217\347\261\263/index.html" +++ "b/tags/\345\260\217\347\261\263/index.html" @@ -1,7 +1,8 @@ 小米 | EricLog -

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 -小米盒子國際版 ...

December 19, 2016
© 2024 EricLog +

小米藍牙手柄

之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。 +...

February 4, 2017

小米盒子國際版開箱

小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。 +小米盒子國際版 ...

December 19, 2016
+ PaperMod
\ No newline at end of file diff --git "a/tags/\345\260\217\347\261\263/index.xml" "b/tags/\345\260\217\347\261\263/index.xml" index cce9a233..81f3a52e 100644 --- "a/tags/\345\260\217\347\261\263/index.xml" +++ "b/tags/\345\260\217\347\261\263/index.xml" @@ -1,4 +1,4 @@ -小米 on EricLoghttps://eric.swiftzer.net/tags/%E5%B0%8F%E7%B1%B3/Recent content in 小米 on EricLogHugo -- gohugo.iozh-twSat, 04 Feb 2017 11:16:49 +0800小米藍牙手柄https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/Sat, 04 Feb 2017 11:16:49 +0800https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/之前提及過只需要在 Windows 安裝 mapper 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能小米盒子國際版開箱https://eric.swiftzer.net/2016/12/xiaomi-mi-box/Mon, 19 Dec 2016 14:38:02 +0800https://eric.swiftzer.net/2016/12/xiaomi-mi-box/<p>小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。</p> +小米 on EricLoghttps://eric.swiftzer.net/tags/%E5%B0%8F%E7%B1%B3/Recent content in 小米 on EricLogHugo -- 0.139.2zh-twSat, 04 Feb 2017 11:16:49 +0800小米藍牙手柄https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/Sat, 04 Feb 2017 11:16:49 +0800https://eric.swiftzer.net/2017/02/xiaomi-bluetooth-gamepad/<p>之前提及過只需要在 Windows 安裝 <a href="https://github.com/irungentoo/Xiaomi_gamepad">mapper</a> 就能將小米藍牙手柄模擬成 Xbox 360 手掣,試過之後確實可以用到。在 The Crew 可以用手掣操作,在刹掣時手掣還會震動,比起在 Android 用功能還多。</p>小米盒子國際版開箱https://eric.swiftzer.net/2016/12/xiaomi-mi-box/Mon, 19 Dec 2016 14:38:02 +0800https://eric.swiftzer.net/2016/12/xiaomi-mi-box/<p>小米盒子相信大家都聽過,亦都可能用過。不過這次小米香港推出的是「國際版」,用的是包含 Google Services 的 Android TV 而不是客製化的 Android。這個就是國際版和中國版的最大分別,這亦都是應否購買小米盒子國際版的主要考慮因素。</p> <figure> <img loading="lazy" src="PB220816.jpg"/> <figcaption> 小米盒子國際版 diff --git "a/tags/\351\226\213\347\256\261\346\226\207/index.html" "b/tags/\351\226\213\347\256\261\346\226\207/index.html" index 5d82d7a1..7653efa2 100644 --- "a/tags/\351\226\213\347\256\261\346\226\207/index.html" +++ "b/tags/\351\226\213\347\256\261\346\226\207/index.html" @@ -1,10 +1,10 @@ 開箱文 | EricLog -

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 +

OnePlus One + Xperia Z2

最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。 之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。 ...

January 12, 2015

Sony Xperia ion

七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。 ...

September 8, 2012

Lenovo ThinkPad X230

早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。 -...

September 3, 2012
September 3, 2012
+ PaperMod \ No newline at end of file diff --git "a/tags/\351\226\213\347\256\261\346\226\207/index.xml" "b/tags/\351\226\213\347\256\261\346\226\207/index.xml" index 76be9a89..1b1fe30c 100644 --- "a/tags/\351\226\213\347\256\261\346\226\207/index.xml" +++ "b/tags/\351\226\213\347\256\261\346\226\207/index.xml" @@ -1,2 +1,2 @@ -開箱文 on EricLoghttps://eric.swiftzer.net/tags/%E9%96%8B%E7%AE%B1%E6%96%87/Recent content in 開箱文 on EricLogHugo -- gohugo.iozh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> +開箱文 on EricLoghttps://eric.swiftzer.net/tags/%E9%96%8B%E7%AE%B1%E6%96%87/Recent content in 開箱文 on EricLogHugo -- 0.139.2zh-twMon, 12 Jan 2015 00:20:36 +0800OnePlus One + Xperia Z2https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/Mon, 12 Jan 2015 00:20:36 +0800https://eric.swiftzer.net/2015/01/oneplus-one-xperia-z2/<p>最近發現我的 Xperia ion 有時候會誤以為耳機還插在電話,結果電話不會響。隔一段時間後重啟又會變回正常。加上 ion 因為少 RAM 而經常自己 kill app,最後決定換部新的電話。</p> <p>之前一直都想換電話,在上年 10 月中的時候收到訂購 OnePlus One 的邀請,買了一部 64 GB Sandstone Black。後來發現不太合意,收機當晚就決定退貨。後來買了 Sony Xperia Z2。</p>Sony Xperia ionhttps://eric.swiftzer.net/2012/09/sony-xperia-ion/Sat, 08 Sep 2012 11:59:46 +0800https://eric.swiftzer.net/2012/09/sony-xperia-ion/<p>七月中在電訊數碼買了 Sony Xperia ion 兼上台,換走了之前用開的 Motorola Milestone。之前的 Milestone 實在太慢,在接電話時會當機,如果電話響時不覺意地旋轉屏幕會更容易當機,其主因都是 RAM 不足。雖然 Milestone 有實體 QWERTY 鍵盤,但我比較少用。因為不習慣指法,所以都是使用虛擬鍵盤。因此,選購新手機首重屏幕大小,令到虛擬鍵盤的按鍵面積更大。</p>Lenovo ThinkPad X230https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/Mon, 03 Sep 2012 13:27:50 +0800https://eric.swiftzer.net/2012/09/lenovo-thinkpad-x230/<p>早前在學校買了 Lenovo ThinkPad X230。學校只有 Lenovo 和 HP 選擇,聽聞 HP 的手提電腦散熱不良、容易壞(之前在實習時就見過 PCCW 的維修人員為整個辦工室的 HP Workstation 換底板,原因是容易氧化而導致壞機。而這些機只是買了幾個月而已),所以最後鎖定了 Lenovo。</p> \ No newline at end of file diff --git "a/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.html" "b/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.html" index 3820c787..9a457383 100644 --- "a/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.html" +++ "b/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.html" @@ -1,6 +1,9 @@ 香港公眾假期 | EricLog -

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是...

April 27, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已...

December 5, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊...

May 6, 2011
© 2024 EricLog +

更新香港公眾假期日曆

最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。

May 3, 2014

2013 年公眾假期已刊憲

今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是下載 iCal 檔的話就需要再下載並匯入香港公眾假期日曆過。 +...

April 27, 2012

已修正香港公眾假期 Google 日曆權限問題

收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已經設定過權限,如果大家再發現日曆有問題的話請馬上通知,謝謝大家的支持! +...

December 5, 2011

2012 年公眾假期已刊憲

今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊於 10 月 1 日,所以 10 月 1 日及 2 日都會是公眾假期。 +...

May 6, 2011
+ PaperMod
\ No newline at end of file diff --git "a/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.xml" "b/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.xml" index 5ffcf508..05fe64e6 100644 --- "a/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.xml" +++ "b/tags/\351\246\231\346\270\257\345\205\254\347\234\276\345\201\207\346\234\237/index.xml" @@ -1 +1 @@ -香港公眾假期 on EricLoghttps://eric.swiftzer.net/tags/%E9%A6%99%E6%B8%AF%E5%85%AC%E7%9C%BE%E5%81%87%E6%9C%9F/Recent content in 香港公眾假期 on EricLogHugo -- gohugo.iozh-twSat, 03 May 2014 15:46:43 +0800更新香港公眾假期日曆https://eric.swiftzer.net/2014/05/2015-hk-general-holidays-calendar/Sat, 03 May 2014 15:46:43 +0800https://eric.swiftzer.net/2014/05/2015-hk-general-holidays-calendar/最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。2013 年公眾假期已刊憲https://eric.swiftzer.net/2012/04/general-holidays-for-2013-is-gazetted/Fri, 27 Apr 2012 22:12:26 +0800https://eric.swiftzer.net/2012/04/general-holidays-for-2013-is-gazetted/今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是已修正香港公眾假期 Google 日曆權限問題https://eric.swiftzer.net/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/Mon, 05 Dec 2011 21:40:44 +0800https://eric.swiftzer.net/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已2012 年公眾假期已刊憲https://eric.swiftzer.net/2011/05/general-holidays-for-2012-is-gazetted/Fri, 06 May 2011 23:17:45 +0800https://eric.swiftzer.net/2011/05/general-holidays-for-2012-is-gazetted/今日政府公布了來年的公眾假期,所以剛才就為自製的香港公眾假期 Google 日曆更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊 \ No newline at end of file +香港公眾假期 on EricLoghttps://eric.swiftzer.net/tags/%E9%A6%99%E6%B8%AF%E5%85%AC%E7%9C%BE%E5%81%87%E6%9C%9F/Recent content in 香港公眾假期 on EricLogHugo -- 0.139.2zh-twSat, 03 May 2014 15:46:43 +0800更新香港公眾假期日曆https://eric.swiftzer.net/2014/05/2015-hk-general-holidays-calendar/Sat, 03 May 2014 15:46:43 +0800https://eric.swiftzer.net/2014/05/2015-hk-general-holidays-calendar/<p>最近港府公布明年的公眾假期,本網站提供的中英文版香港公眾假期 Google 日曆已加入 2015 年的假期。已經訂閱了的朋友應能在 Google 日曆中自動看到 2015 年的假期。</p>2013 年公眾假期已刊憲https://eric.swiftzer.net/2012/04/general-holidays-for-2013-is-gazetted/Fri, 27 Apr 2012 22:12:26 +0800https://eric.swiftzer.net/2012/04/general-holidays-for-2013-is-gazetted/<p>今日政府公布來年的公眾假期,我已經馬上更新了中英文版的公眾假期 Google 日曆。如果大家是直接訂閱的話,2013 年的假期會自動顯示在你的日曆中。如果是下載 iCal 檔的話就需要再下載並匯入<a href="http://holidays.swiftzer.net/">香港公眾假期日曆</a>過。</p>已修正香港公眾假期 Google 日曆權限問題https://eric.swiftzer.net/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/Mon, 05 Dec 2011 21:40:44 +0800https://eric.swiftzer.net/2011/12/hong-kong-public-holidays-calendars-permission-problem-fixed/<p>收到 Janice 的通知,發現兩個 Google 日曆的事件都只能看到「Busy」的字樣,完全看不到假期的名稱。剛才檢查過,發現兩個日曆的權限確實被更改過。不過現在已經設定過權限,如果大家再發現日曆有問題的話請馬上通知,謝謝大家的支持!</p>2012 年公眾假期已刊憲https://eric.swiftzer.net/2011/05/general-holidays-for-2012-is-gazetted/Fri, 06 May 2011 23:17:45 +0800https://eric.swiftzer.net/2011/05/general-holidays-for-2012-is-gazetted/<p>今日政府公布了來年的公眾假期,所以剛才就為<a href="http://holidays.swiftzer.net/">自製的香港公眾假期 Google 日曆</a>更新。2012 年的農曆新年會在一月尾,而最特別的是中秋節翌日及國慶日是重疊於 10 月 1 日,所以 10 月 1 日及 2 日都會是公眾假期。</p> \ No newline at end of file