2012年12月27日 星期四
[iOS] 利用Smart App Banners在網頁上推廣Native App
好像該寫個開箱文 - 白蝴蝶
這不是我第一支預購的自己公司的產品, 第一支是給老婆的Sensation, 這算第二支, 為啥堅持要預購, 還追那麼勤勞? 1. 自己用過一段時間, 這的確是好產品! 2. 從預購開始, 才能真正體驗完整的使用者體驗呀... (剛掰的) 3. 拿好手機在街上走不用遮遮掩掩的....
不過這次還真一波三折, 經歷了貨等不到, 換了顏色(本想買紅的), 又拿到亮點機, 都已經跟店員打熟了....
2012年11月25日 星期日
[開箱] 今年的生日禮物 Tissot PRC 200 機械錶
本來是看SEIKO, CITIZEN, 看金城武跟王力宏代言看起來蠻帥的, 日本錶功能又多, 科技感濃厚, 就有點感興趣, 不過直到昨天, 跑去看007電影Skyfall, Bonds戴的Omega也蠻帥的呀, 不過自己平常對錶沒啥研究, 不知道, 這還真是我買不起的高貴, 買不起Omega還是繼續跑去專櫃看了SEIKO, CITIZEN, 實際上看了實物, 還是失望了, 這不是我想要的型, 才發現我喜歡的並不是這麼濃的科技感, 而是比較古老一點的機械
再逛了一下瑞士的錶款, 還真一個比一個高貴, 好像只有TISSOT是外型跟價位還是我可以接受的, 所以今天就找了老婆, 小遠跟我去敗了這隻 - TISSOT PRC 200機械錶, 這還真是我戴過最貴的(羞), 也是第一隻機械錶
紅黑色的外盒:
2012年11月13日 星期二
[Go筆記] UUID package
Source在此: https://github.com/julianshen/GoUUID
目前只implement RFC 4122 Version 4
Sample:
2012年11月7日 星期三
[node.js] 在heroku上用bower管理前端第三方元件
舉一個例子,你可以利用bower來安裝jQuery :
bower install jQuerybower會把jQuery 安裝到"components/jQuery" 目錄裡
在npm , 你可以把所有相關的模組設定在package.json中,這樣就不用一個個下"npm install mypackage"這樣的指令了,bower也有類似的設計,只是為了避免跟package.json造成混淆它採用的檔名是component.json,內容跟package.json差不多:
{在Heroku deploy node.js app有個很方便的地方是,當你做git push 後,它會根據package.json自動幫你安裝相關的node.js 模組,但不幸的是它並不支援bower,只支援npm
dependencies: {
"JQuery": "*"
}
}
還好Heroku很有彈性,可以藉由buildpack來自己加這部份
buildpack API其實還蠻簡單的, 基本上只要準備三支shell script:
- bin/detect
- bin/compile
- bin/release
- 新建的程式使用自己的buildpack:
heroku create myapp --buildpack https://github.com/heroku/heroku-buildpack-ruby - 既有的程式更換buildpack:
heroku config:add BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby
- 安裝bower
- 找出所有的component.json並用"bower install"安裝套件
- 自己電腦上安裝bower或lodash都沒問題
- 在自己的package.json加上lodash, deploy到Heroku也一樣掛
- 在package.json裡設定engine裡的npm版本到最新的1.1.65(跟自己電腦上一樣版本), 也一樣不行
那...就不得不懷疑是npm問題了, 查了原本buildpack寫的, Heroku的node.js和npm其實是拿他們自己放在S3的binary package來安裝的, 可疑!
因此就使用了下一招 - 用npm來裝npm
用Heroku系統內的npm來裝npm是可行的, npm install npm的結果是, 會有一份最新的npm被安裝到node_modules底下, 可以用"node_modules/.bin/npm"來呼叫這版本, 因此就加上了:
node_modules/.bin/npm install bowerBingo! 這樣可行, bower一樣被安裝到 node_modules/.bin底下, 加上 "2"的邏輯到"bin/compile"後就大功告成了,
有修改的部份只有"bin/compile", 比對一下commit history就可以知道我修改過啥, 中間有做了很多實驗, 所以有不少垃圾commits, 可以忽略
如果有需要這版本buildpack, 也歡迎利用:
https://github.com/julianshen/heroku-buildpack-nodejs
2012年11月5日 星期一
[node.js] 用npm安裝放在github上的module
改完後的一個問題是, 原本可以用"npm install passport-flickr"來安裝這module, 但由於我fork出來的並未註冊在npm, 所以就得用另一個方式:
npm install git://github.com/julianshen/passport-flickr.git#master這方式是直接把git url給npm, 不過要deploy上heroku就麻煩了, 還好寫在package.json裡也是沒問題的
"dependencies": { "passport-flickr": "git://github.com/julianshen/passport-flickr.git#master"}這還蠻簡單的, 只是把原先寫version的地方換成git URL就好, 在Heroku上也是沒問題的
題外話: 剛deply到heroku上時發現express 3.0.1無法安裝, 原因是我忘了在package.json裡註名我node.js的版本, 這種狀況下, Heroku會自動幫你挑v0.4.7版
2012年11月3日 星期六
竹北。梨頭山
View Larger Map
住這邊這麼久了, 說真的, 還沒來過這, 碰了運氣找到了入口, 感覺好像還真是個人煙罕至的地方
太久沒啥運動, 這坡度爬起來還真有點喘, 也不知道該爬多高才可以看到高鐵, 其間曾一度在樹林縫隙看到了軌道, 不過這並不是最佳位置, 到了山頂上, 果然名不虛傳呀, 高鐵站周邊景色一覽無遺
在這邊用長焦段的鏡頭並不難捕捉到高鐵的身影, 不過新買的反射鏡實在令人失望, 不但不好對焦畫質也差, 只好用super takumar 135mm這隻俗又好用的鏡頭
這邊的景致還不錯, 而且離我家非常的近, 可以俯瞰高鐵站
下次有機會再來去湖口另一個拍攝地點看看好了....
2012年11月2日 星期五
[Go 筆記] Type, method, and interface
"Go was born out of frustration with existing languages and environments for systems programming."Go 是為了作為一個系統語言(systems programming language)而存在的, 它跟天下知名的C語言有同一個父親 - Ken Thompson , 作為一個系統語言, 它並不是直譯式語言, 也非跑在虛擬機(virtual machine)上, 而是貨真價實的像C一樣是先編譯(compile), 而且也是屬於strong and static type的語言, 這表示, 變數型別是預先宣告/決定的, 而且是不能半路變更的, 變數可以像這樣宣告:
var StrVariable string這邊跟C, Java之類的語言不同的地方是, 型別定義是在後面不是放前面, 剛看到時我以為我會不習慣, 但卻花沒很多時間就適應了, 可能是我在很早以前寫過PASCAL的關係吧, PASCAL也是類似的寫法
var IntVariable int
但, Go其實還引入了一些dynamic language的特性, 因此在Go內也可以看到未經上面類型的宣告, 就直接指定的敘述, 像是:
strVariable2 := "Hello Go"這敘述並不代表Go也有dynamic typed的設計, 其實這設計是同時結合了宣告跟指定(assignment), strVariable2並不是沒有型別或動態型別, 因為":="的關係, 使得strVariable2一開始就被宣告成後面值的型別, 因此上面那行跟下面一樣:
var strVariable2 string同理, "intVariable2 := 1"這敘述表示intVariable一開始就被宣告成整數(因為1是整數), 所以它還是一個strong typed的設計, 這邊比較要注意到的一個陷阱是"="和":=", 在已宣告過型別的變數, 是用正常的"="來指定值, 但在未宣告的變數, 必須要用 ":="
strVariable2 = "Hello Go"
你不能做的是... "strVariable2 = 1", 因為strVariable2在前面已經":="被宣告成字串(string)
在Go, 你一樣可以自定型別, 結構(struct)
type MyString string
type User struct {之後, 你就可以用MyString或是User來宣告變數, 像是
Uid int
Name string
}
var M1 MyString"type MyString string"有點像是C語言的typedef, 可以讓你以另一個型別(MyString)來替代原本的型別(string), 但它其實還有個妙用, 在後面會再提到
var User1 User
User2 := User {1, "julian"}
Go是一個functional programming language, 所以它並沒有物件導向觀念
沒有"類別"(class), 沒有物件/實體(object/instance), 但, 它卻有"方法"(method)和"介面"(interface), 聽起來有點四不像, 但其實這部份還蠻有趣的
先說到"方法"(method), 在Go, 你可以為你的型別設計一個"方法", 像是這樣:
func (u *User) Hi() {這邊"Hi()"就是屬於"User"的一個方法, 呼叫"User2.Hi()"即可執行它, 方法跟一般的函數一樣, 所不同的是, 前面多了一個"(u *User)" 以這例子來說, 這邊就是定義了"Hi()"的母體是 *User (*是指標- pointer的意思, 就不在這邊解釋)
fmt.Printf("Hi! I'm %s. The %d user.\n", u.Uid, u.Name)
}
這邊有趣的地方是, 你可以為任何自定型別創造"方法", 包含函數(function), 這在"net/http"內就可以找到類似的應用:
在這例子中, HandlerFunc有一個ServeHTTP的方法, 但HandlerFunc本身其實是一個function
"介面"(interface)也是一個蠻妙的東西, 在Java裡, interface必須要宣告"實作", 亦即"class MyImpl implements MyInterface", 也就是你必須指定某個class實作了某個interface
但在Go, 則是很不一樣, 在Go裡, 你可以宣告一個"介面"(interface) 像是
type MyInterface interface {但你不需要宣告某個型別"實作"了這個interface, 在Go, interface反而比較有"暗示"(imply)的意味, 也就是, 下面的例子, 不用任何宣告, 你可以把MyString自動當成它也可以是一個MyInterface:
Foo()
}
func (s MyString) Foo() {}因為在MyInterface的定義中, 它含有一個"Foo()", 當你替MyString宣告了一個"Foo()"的方法時, 就自動讓MyString變成了一個實作了MyInterface的型別
下面用一個比較完整的例子來總結:
在這例子中, "CallFoo(MyInterface)"接受一個MyInterface的參數, 並執行它"Foo()"的方法, 由於"Aa"(字串)和"MyHandler"(函數)都是有一個"Foo()"的方法, 所以他們都可以被當做MyInterface作為參數, 同理, 回到前一個HandlerFunc範例, HandlerFunc其實也可以被當做Handler這個interface來使用, 在"net/http"的API中就是用到了這樣一個技巧
2012年10月31日 星期三
[Go筆記] Heroku, Facebook
import facebook "github.com/julianshen/FacebookGoSDK"這樣是import一個在github上的package, 當你使用"go run"或"go get"之類的來編譯執行程式時, Go自動會上github幫你把這些相關的package取回, 用這個Heroku buildpack做出的環境一樣適用
第一次用impress.js做的投影片: Introduction to node.js
Impress.js
這跟一般投影片製作軟體很不一樣, 正確說來它是一個javascript framework而非一個完整的軟體, 所以也沒啥GUI(似乎也有不少人幫它寫editor), 不過基本說來, 用它來做投影片的方式, 就是拿source codes裡的html檔來改就對了
出來的結果很炫, 但編寫的過程有點小累, 所以後面有點偷懶, 不過也完成了明天要講的投影片:
Introduction to node.js
寫完了之後, 想把它開放出來, 我之前放投影片都依賴了slideshare, 不過slideshare並不能吃這種檔案呀! 所以host又是另一個問題, 所幸, 還有developer的好朋友 - github
github有個好東西叫page, 可以讓你放一些介紹網頁, 也可以有自己的host name像是"julianshen.github.com", 只要照下面這網頁內的說明, 就可以建立屬於自己project的"page":
Creating Project Pages manually
2012年10月24日 星期三
[Go筆記] Go on GAE: 比"Hello World"多一點點: 處理static files
"Go is an attempt to combine the ease of programming of an interpreted, dynamically typed language with the efficiency and safety of a statically typed, compiled language. It also aims to be modern, with support for networked and multicore computing. Finally, it is intended to be fast: it should take at most a few seconds to build a large executable on a single computer. To meet these goals required addressing a number of linguistic issues: an expressive but lightweight type system; concurrency and garbage collection; rigid dependency specification; and so on. These cannot be addressed well by libraries or tools; a new language was called for."簡而言之, Go的目標是一個簡單又有效率的語言, 加上一開始設計就考慮網路以及多核, 就姑且說它適合server端的開發吧
事實上, GAE(Google AppEngine)也是有支援Go的 (怎麼說呢, 好歹這也是Google自己親生的呀)
那, 現在開發server端的工具那麼多種, 有什麼樣的理由是選Go優於其他解決方案呢? 以效率來說, 或許比較好吧, 既然它是原生碼, 應該會快一些吧, 但這也沒啥好的benchmark來佐證, 劣勢呢? 以node.js來相比好了(今年看比較多這個, 直覺拿這來做比較), 它不但沒既有的developer做基礎(既有的javascript developer是很大一群的), 也沒有數量很多且快速增長的第三方模組可供利用(雖然, 盲目用npm上的模組, 某種程度上要承擔一定的風險), 最大的問題就是...."文件"
以上前半段是抱怨文, 不過文件不足也沒辦法當啥藉口, 這年頭...."做就對了".....GAE上關於Go的文件也不是沒有, 跟著做, 就可以做出"Hello World"了, 但剛做完後, 突然楞住想....那...我的html檔案放哪裡? 我總不能每個文件都寫在程式內, 總會有靜態檔案的呀! 有點讓我回到十幾年前寫CGI的感覺
GAE的文件還真找不到, 但在Go的文件http.FileServer的範例可以找到解答, 但要把"FileServer"這個interface跟這件事聯想在一起還真有點不容易, 不過反正就是在程式內要加入下面這行來處理靜態檔案
http.Handle("/", http.FileServer(http.Dir("./static")))這範例讓你可以把靜態檔案放在"static"目錄下, 假設你在static目錄下放一個a.html, 那這行的目的就是可以讓你用 http://your_app_host/a.html來存取它
我起始一個範例放在github上, 從這可以看完整的目錄結構, 未來相關範例也會放在這邊
2012年10月23日 星期二
[Go筆記] defer
try {
.....
} finally {
in.close();
}
在finally區塊內去做一些善後的工作, 以確保這些善後工作可以在程式執行後不管有沒錯誤發生都可以被執行到, 在Go中, 用的則是defer:
defer後面接的可以是一個closure, 這些closure會在函式執行結束後(return 之後)緊接著被執行, 所以這段程式執行的結果會是這樣:
6becnna--------1bea
[Go筆記] Do not communicate by sharing memory; instead, share memory by communicating.
Do not communicate by sharing memory; instead, share memory by communicating.這是在Go的concurrent programming裡一個很重要的精神, concurrent programming也是Go裡面一個相當重要的部份, 它已是語言本身的一部分
"channel" 也就是這精神下的產物, channel可以說是多個thread之間溝通的工具, 有點像是"pipe"但又有點肩負多執行緒間synchronization的責任, 下面實作了一個concurrent merge sort (我想這例子蠻適合的)
"go"這個修飾字用來啟用一個goroutines, goroutine可以視為一個輕量的執行緒(lightware thread), 在這用了一個內容是整數陣列的channel: chan []int, channel必須要先用"make"初始化
由於使用了"go MergeSort(data[:middle], leftChan)", 因此, 左右兩邊的merge sort是同時進行的, 直到兩邊都結束後才會執行"merge"的動作, 但用了"go"啟用了執行緒, 連續這兩行是不會卡住(block)等結果的, 那怎確定兩個執行緒都真的結束後才執行merge的動作? 這其中就是透過channel來做同步化的動作, "ldata := <-leftChan", 這一段是將leftChan這個channel的結果指定到ldata, 如果leftChan一直沒輸入(input)進來, 這一行就會等在這, 直到左邊的MergeSort執行到Merge結束後(r<-)
主程式也是叫起一個執行緒做MergeSort的動作, 等到結果產生才將結果印出, 整個不是很難懂, 但這思維還有點難調整(對我來說)
[Go筆記] Go語言中的Closure
在Go裡, function是可以像在javascript中一樣, 被指定到一個變數來使用:
上面兩個範例, 一個是把函式指定給一個變數, 一個是來當做回傳值, 這也是Go的closure大致上的形態, 在Go Tour的第48頁的作業, 便是要求利用closure來完成Fibonacci數列的計算, 我們所熟知的Fibonacci數列(0, 1, 1, 2, 3, 5, 8 ... f(n) = f(n-1) + f(n-2))可以用iterative或recursive的方式來求解(範例), 當然使用Go其實也可以採用這兩種方式:
Iterative solution:
Recursive solution:
如果單以公式看, Recursive solution是比較容易被理解的, 不過也比較有stack overflow的風險, 當然, 照這練習題的要求, 以上兩者都不滿足, 那改用closure來實作又是怎樣?
整體上還算蠻簡潔的, 既然closure在javascript也很常被利用, 這邊放一個javascript的版本做一下比較:
測試了一下效能, 前三種實作方式, 感覺不出有太大的差異, 也就很難說誰優誰劣, 至於Go與javascript的比較, 如果是以"go run "的方式跟node.js來比, node.js高出甚多, 可能"go run"還是需要經過編譯階段, 但如果先以"go build"編譯成原生碼(native code), go的效能就高出甚多了, 不過以這比較也不算準啦....
2012年10月8日 星期一
在node.js使用underscore.js
underscore.js對javascript來說是一個蠻好用的工具, 在前端應用程式的開發中也很常被利用到,但同樣以javascript為基礎開發的node.js, 能否直接使用underscore.js呢?
嘗試以下面的程式碼用node.js跑:
require('./underscore.js'); var a = [1, 23, 6, 11, 25, 12, 33, 11, 4]; _.forEach(a, function(v) { console.log(v); });
執行結果會得到"ReferenceError: _ is not defined"
打開underscore.js的原始碼一看, 發現了這一段, 其實它是有針對node.js做手腳的:
// Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object via a string identifier, // for Closure Compiler "advanced" mode. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root['_'] = _; }
也就是, 在browser時, "_"是一個global object, 但在node.js則不是, 原本的範例如果改成這樣就可以了:
var _us = require('./underscore.js'); var a = [1, 23, 6, 11, 25, 12, 33, 11, 4]; _us.forEach(a, function(v) { console.log(v); });
這樣一來, 前後端也可以共用同一份underscore.js了
補充: 類似的工具還有async這個, 也是前後端都可以應用
----- 10/11 補充 -----
寫這篇的時候真的是沒睡飽, 沒注意看underscore放在github上的內容, 它已經弄好node module了, 所以只要:
npm install underscore就可以裝好了, code裡面也只需要用"var _us = require('underscore')"
2012年10月3日 星期三
一些小想法 10/4
今年參加了好幾個國內外的hackthon,收穫還算不少,覺得,很多developers都有很多不錯且創新的想法,但實際上,大部份的工作場,除了以developers為主體的公司外,大部份的developers處的角色都是執行者,這些"想法"反而往往在工作場合不會出現或被埋沒,這讓我想到"Effective Programming"裡提到的"developers大多不善於表達"!
這倒很難辯駁的,這一行,包含我自己,不用說不懂的做美美的簡報、炫麗的demo,連把一個簡單的觀念傳達給別人都可能要花上大把時間,尤其是傳達給非技術相關的,developers的思考模式大多是非線性的、跳tone的,強一點的根本就是multi cores,而且,都是沒耐性的,所以往往傳達給別人的,大多不到本意的六成,被瞭解的大概又只剩這一半
Hackthon算是一種不錯的訓練,訓練人在短時間團隊合作並傳達更多有趣的想法,雖然我不是很喜歡那些藉這種場合宣傳自己產品的,畢竟他們的產物不是在當下產生的,不過也是很多有趣的startup就這麼興起的
2012年9月7日 星期五
[Android] GCM可以怎麼用?
其實是可以不用的
從API console可知, 是可以設定允許從任意IP傳送的
再來看看一般的流程:
1. Regster:
2. 取得Registration ID後將ID送至ServerGCMRegistrar.checkDevice(this); GCMRegistrar.checkManifest(this); final String regId = GCMRegistrar.getRegistrationId(this); if (regId.equals("")) { GCMRegistrar.register(this, SENDER_ID); } else { Log.v(TAG, "Already registered"); }
3. Server之後可以用這ID送訊息給這一支手機
由"2"看來, 其實Registration ID是關鍵, 而不是Server, 任何人只要有Registration ID, 就可以傳送訊息給這隻手機了
做了個小小實驗, 寫了一支Activity同時具備了收訊息以及傳送(給自己)的能力:
https://github.com/julianshen/GCMTest
想到的應用: 兩隻手機透過條碼或藍牙交換Registration ID, 就可以透過GCM互傳訊息了
缺點: Registration ID如果有變(GCM是有可能更換ID), 就失效了
[Android] 如何知道使用者是否已移除你的程式
http://stackoverflow.com/questions/12245163/android-sdk-facebook-how-to-know-when-a-user-is-not-using-your-app-anymore/12250465#12250465
我也提供了一個解法, 不過, 慚愧的是, 我當初突然想到這解法後, 自己並沒有去實驗它, 只是覺得理論上可行... :P
我的方法是, 利用GCM (Google Cloud Messaging), 裡面有一段:
When users uninstall an application, it is not automatically unregistered on GCM. It is only unregistered when the GCM server tries to send a message to the device and the device answers that the application is uninstalled. At that point, you server should mark the device as unregistered (the server will receive a NotRegistered error).
後來拿了GCM sample試了一下, 好像也可行, 只是不是那麼即時, 也就是沒辦法剛移除就知道了, 我的測試結果是, 一兩個小時候才出現 "NotRegistered", 不過似乎也是個解法就是了...
2012年9月2日 星期日
[筆記] 2012 9月第一週
node for Android
這週本來預定的目標是串接上NativeActivity, 不過, 沒做到, 需要多花點時間想切入點:- pass "struct android_app *" to Node (從android_main呼叫新的node::Start)
- wrap "struct android_app *" (是否要依附在process object? 參考資料: node_object_wrap.h, v8::Object::SetPointerInInternalField)
- Design of callbacks for AppCmd, InputEvent (NativeActivity --> Node)
adding native module to Node:
為了練習這部份, 把android log的機制加進去, 完成後可以在js裡用下面這樣叫用Android log
require('android');由這code看, 主要有兩個部分, 一個是一個名為android的module, 一個是alog這個global object (Node本身已有"log", 為避免衝突, 改名為alog)
alog.d('tag', 'message1'); //debug log
alog.e('tag', 'var a=', a, ' found', 1, 'error'); //error log
新增module的參考資料為: http://nodejs.org/api/addons.html#addons_hello_world , 不過, 這邊加的不是addon而是一個native module, 所以有點不同, 首先為了這個module新增了, node_android.cc (class Android), 名字一定要是"node_"開頭 , 這是由於binding時, 會尋找"node_"開頭的module (參考 node_extensions.cc : get_builtin_module), 這邊主要implement的是用"NODE_SET_METHOD"加入新的method "log", 用"NODE_DEFINE_CONSTANT"映射了幾個log level的常數, 最後用"NODE_MODULE"定義module的init method
Source code: https://github.com/julianshen/node-android/blob/master/jni/node-v0.8.8/android/node_android.cc不過這樣不夠, 因為我要的不是只有"log", 而是要像"alog.d"這樣的東西, 因此又多了一個android.js來處理:
var binding = process.binding('android');
function log(level, args) {
var tag = args[0];
var len = args.length;
var objs = [];
for(var i = 1; i < len; i++) {
objs.push(args[i]);
}
var msg = objs.join(' ');
binding.log(level, tag, msg);
}
var alog = {
v: function() {
log(binding.ANDROID_LOG_VERBOSE, arguments);
},
d: function() {
log(binding.ANDROID_LOG_DEBUG, arguments);
},
i: function() {
log(binding.ANDROID_LOG_INFO, arguments);
},
w: function() {
log(binding.ANDROID_LOG_WARN, arguments);
},
e: function() {
log(binding.ANDROID_LOG_ERROR, arguments);
}
};
global.alog = alog;
首先用了process.binding來bind剛剛native的部份, 另為就是把alog變成global object
其他TODO
Idea board, Bluetooth OPP....還沒想fb world hack做啥好2012年9月1日 星期六
2012年8月26日 星期日
node.js for Android
實際是,還是有點問題, 需要改些東西, ndk的build system可以說是原本Android的簡化版, 所以有些東西並沒有, 所以並不是把原本的Android.mk copy過來就沒問題了, 比如說v8, LOCAL_GENERATED_SOURCES, intermediates 這些東西雖然有在原本aosp的build system內, 但在NDK就完全不存在, 但由於v8和node都有需要把js lib轉成CPP一起build進去, 所以這時候就要借助這類的東西:
$(shell python $(JS2C_PY) $(GEN1) CORE off $(LOCAL_JS_LIBRARY_FILES))
起初我並沒注意到js2c這部份, 結果最後link不過, 缺了一堆東西, trace之後才發現少這部份
此外, 在uv的部份, 由於uv是node的native library的部份, 有一些OS相關的code, 但由於Andorid的bionic比起libc是算簡化版本, 有些API並不存在, 因此除了為android產生一些專屬的config.h (我從linux的版本去改的), 還要改一些code
目前的進度是, 可以用ndk-r8b build出Android可用的可執行檔(還沒完全測過, 應該有些bug代解), 先全用static link的
Source codes放在GitHub : https://github.com/julianshen/node-android
還有一些想做的還沒做:
- 接上NativeActivity
- 透過AssetManager存取js files
- OpenGL ES apis
- Android apis
- 其他? (Packaging tools and etc..)
2012年8月10日 星期五
Firefox OS on HTC Sensation
Firefox OS是基於Android 4.0 ICS, 所以照理說有支援ICS的裝置應該是沒問題才對, 所以就打算從CM9 (Cyanogenmod 9)的source來po, 至少它已經把ICS這段做完了
以下是我抓來用的source:
For HTC Sensation (使用Qualcomm msm8660):
https://github.com/CyanogenMod/android_device_htc_pyramid
https://github.com/CyanogenMod/android_device_htc_msm8660-common
For Qualcomm hardware:
https://github.com/CyanogenMod/android_hardware_qcom_display
https://github.com/CyanogenMod/android_hardware_qcom_gps
https://github.com/CyanogenMod/android_hardware_qcom_media
還有改了一些東西, 像是"frameworks/base/libs/ui/" 裡的東西, 不過實際改了哪些, 還沒整理(也很懶), 就有點忘了, 反正就讓這些build過, 然後讓"b2g"可以成功的跑起來, 差不多這樣就夠了
一開始把system.img燒進去後, 發現不能正常開機, adb一直說找不到/system/bin/sh, 我裝了4ex recovery去看, 居然無法mount system partition, 所以我就改變策略, 想說做成update package來安裝, 但build update package時發現, b2g做了些手腳, 像是把java導到一個fake java去, 使得我沒辦法順利build 出update package, 所以再動了些手腳, 把東西做出來, 然後用4ex recovery format system partition之後從SD card裝update package (這個我動過手腳重包過, update script裡面會刷full的system image), 但重開機後...黑屏.....看log研究一下發現, 我一開始只放了device目錄裡面的東西, hardware目錄裡面的東西都沒放, 所以build出的東西缺少了qualcomm的lib, 所以又去抓來重build, 但後來黑屏還是沒解掉.....但卻發現一點...之前燒進去的system image之所以有問題似乎是Sensation的hboot似乎不吃sparse image, 所以最後用simg2img (build好的host binary可以找到) 轉成一般的image改用zip燒, 所以就又放棄update package這條路
接下來是我這整個過程中我幹的最蠢的事, 開機可以成功, 但卻一直是黑屏, 一直搞不懂到底哪出了問題, 還抓了kernel source, 一路從底下看到b2g....framebuffer也dump出來看, 一切都沒問題呀!今天, 終於發現, 我少做了最後一個步驟!
我忘了install Gaia了(以下步驟):
我是怎發現的?因為我後來自己build emulator...也是黑屏呀...想說到底哪不對, 連emulator都打算去trace了....後來查了一下才發現, 我還真的少做這步驟呀....浪費了好幾天.... orzcd B2G/gaia GAIA_DOMAIN=foo.org make install-gaia
反正成功了.... update package那條路搞不好也是通的, 其實這樣一來應該不用做full image, 做一包來patch應該也ok, 搞不好就只需要改init.rc跟放/system/b2g/的東西再install gaia就好了....不過也懶得試了....之後再說吧.....
2012年7月19日 星期四
IFTTT
[ADK] 連接ADK2012到MacOS Lion
我家裡的電腦是Mac mini, OS是Mac OS Lion, 如果照著ADK2012的開發文件想要去設定環境來開發, 一開始就會碰壁了
在文件中寫著, 你可以從"repo init -u https://android.googlesource.com/accessories/manifest"下載原始碼, 或是下載Mac版的ADK 2012 IDE (應該是由Arduino IDE改的)來開始開發
但不管是IDE也好, 還是照著command line的步驟來做, 都會得到無法在找到/dev/ttyUSB0的錯誤(在IDE裡面也根本看不到這個), 剛用估狗大神查了一下, 有人討論, 但也沒有答案, 最後是在ADK2011的文件裡找到解答, 差的就是FTDI USB Driver, 其實照它講的位置也沒找到mac版本的driver, 只有windows版本的driver, mac版本的可以到FTDI chip的官網下載
裝完FTDI driver後, 打開IDE就可以看到USB serial的位置了
在我電腦的例子是/dev/tty.usbserial-AH015SE1接下來如果到"android-accessories/adk2012/board/MakefileBasedBuild"去在重build一次並重燒, 這次會發現, 在做
$> ./flash
這步驟時還是會失敗, 這是因為在這script裡寫死了
UART=/dev/ttyUSB0
所以UART的位置不是這個就會有問題, 因此我改成下列的方式燒
$> android-accessories/ADK2012.app/Contents/Resources/Java/hardware/tools/adk2tool /dev/tty.usbserial-AH015SE1 erase
$> android-accessories/ADK2012.app/Contents/Resources/Java/hardware/tools/bossac --port=tty.usbserial-AH015SE1 -w -b app/bin/test_sam3x_ek_sam3x8-flash.bin
$> android-accessories/ADK2012.app/Contents/Resources/Java/hardware/tools/adk2tool /dev/tty.usbserial-AH015SE1 reset
要照這順序執行, 一開始我不小心erase掉了後, 燒不成功(因為bossac少加了 --port), 整台黑漆漆的不動, 嚇了我一身汗, 以為把它搞掛了... :P
好吧, 可以開始亂改看看了
[Android] ADK 2012 偽開箱
昨天去了GTUG聽了關於Arduino的分享, 硬體我是大外行, 但這東西還真的是很有趣呀! 其實在Google I/O拿到了免費的ADK 2012, 目前也還沒想到除了高級鬧鐘外, 我要拿它來玩啥, 先來個開箱好了...呃...但其實我老早就把箱子給破了....好吧, 這是個偽開箱
整個東西其實是用磁鐵吸住的, 所以要把這鬧鐘箱打開, 其實很簡單, 打開後分為兩塊版, 上面那塊版據說是Arduino Due, 下面則是一堆Sensor
這一塊就是Due, 後端有SD
前端則有兩個USB加上電源, 它可以由USB供電, 也可以由這個電源供電, USB一個是連接PC用, 一個則是手機
Sensor則是有溫濕度感應, 大氣壓感應, 光感應, 顏色, 加速度, 磁力, proximity sensor等等... (呃, 這樣還是個鬧鐘嗎?)
當然也有NFC:
整個合體的外觀就像這樣:
當然, 這不是普通的Arduino, 這是ADK 2012, Android手機可以透過USB或藍牙與它連結
Android developer網站上有開發簡介: http://developer.android.com/tools/adk/adk2.html
也有一個範例程式, 可以從Play store上下載
不過還沒想到該拿它來做啥, 不過以這形狀, 如果想拿來做個啥機器人之類的, 就有可能會需要把它給拆了吧?
不過如果能做一個這樣的東西, 也不賴呀:
2012年7月17日 星期二
[Android] Enable/Disable Activity at runtime
這篇主要來自Google I/O 2012這兩個Sessions:
Making Good Apps Great: More Advanced Topics for Expert Android Developers
Doing More With Less: Being a Good Android Citizen
在這兩堂演講中有提到, 可以利用"PackageManager.setComponentEnabledSetting"把不用的Intent receiver給停用, 這在Android中的確相當有用處, 常常Intent broadcast都是一個造成系統忙碌的主要原因, 在早期的Android版本, 甚至常常碰到因"android.net.conn.CONNECTIVITY_CHANGE"所導致的ANR, 這是因為註冊聽這Intent的receiver常常非常的多(大多網路相關, 而智慧型手機最常見就是網路相關的軟體)
不過這邊要講的並不是這個問題, 不過也可以應用到同樣的技巧
Android中, 可以設定多個Activity去處理同一種的Intent, Share (android.intent.action.SEND)就是最典型的例子, 但這也造成另一種困擾, 註冊的應用程式太多, 導致整個Share menu相當的雜亂難選:
由這個例子看來整個list相當的長, 這對使用者來說可能不好用, 而且問題在於, 有些可能不用一開始就出現, 比如說如果沒有登入任何一個e-mail account, Mail這選項是可以被藏起來的, 這作法就是
ShareActivity 預設成disable:
<activity android:name="ShareActivity" android:enabled="false">
這樣如果沒enable它的話, 就不會在Share list出現, 只要我們在某個點(比如說已經完成登入帳號)去enable它, 它就又可以回到這list了:
if(pm.getComponentEnabledSetting(cname) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED ) {
pm.setComponentEnabledSetting(cname, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
} else {
pm.setComponentEnabledSetting(cname, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
}
2012年7月8日 星期日
Google IO 2012
尼克大說,對Julian的十大IO問題之一應該是"你是自己花錢去的嗎?", 今年的確自費出國參加了幾個活動, 本以為今年也無緣IO了, 不過最終還是成行了, 不用繼續怨念, 這都得感謝很多人呀…首先是Tiffanie的幫忙牽線, 以及Elvis的慷慨讓票(老實說他大可以拿去賣黃牛票賺一筆,卻原價轉賣給我), 還有wuman, 不曉得幫我跟他朋友煩過幾次, 後來聽到尼克大也差點幫我弄到票, 實在非常的感動....還得感謝Arthur, 我這次就可以不用自費出去了(還得間接感謝這時候離職的Johnny :P , 還有也有幫我問的mifong), 反正終於不用在台灣枯坐在電腦前熬夜看轉播, 雖然去了美國還是熬著夜被公司這邊要東西(不是有人拿著保障名額出去的?), 好吧....總歸還是很高興....很感謝這麼多的人....
好了, 感謝完了, 進入正題, 猶豫半天, 還是把這篇當半遊記來寫, 反正好像還在Jet lag (呃, 都回來三四天了), 還睡不著, 適合胡言亂語, 寫寫流水帳
2012年6月22日 星期五
2012年5月9日 星期三
使用HTML5/Javascript做一個像Instagram的相機 (Only works on Opera mini)
Instagram是一個我一直蠻喜歡的service, 主要是簡單, 加上有一些濾鏡可以豐富我隨手拍的照片, 當然, 重要的是, 高價賣給了Facebook而一炮而紅
上星期, 參加了AT&T Palo Alto Hackthon, 拿到大獎的團隊用了一個lib叫 caman.js 的, 這東西讓我有點小小驚艷, 它光用javascript (其實是Coffee script)就實作出了許多影像處理的功能, 這讓我興起想用這個來試著做出類似Instagram的東西, 當然是純用HTML+Javascript
首先面臨的一個問題是, 實作Camera的部份, HTML5支援media capture的方式有三種(請參考Reference 3) : Input tag, device tag, WebRTC (getUserMedia)
但很不幸, 除了Input tag以外, 大部分手機上的browser,如Android browser, Firefox, Chrome, 可以說幾乎全部都不支援, 這可真是有點令人傷心的消息, 因為用Input tag, 會離開browser跳到另一個程式, 這樣就無法結合自己的UI了
不過, 其實也沒那麼絕望, Android上的Opera Mini, 就支援getUserMedia (參考Reference 1)
因此就可以實作出像這樣的東西:
WebRTC(getUserMedia)的原理是把media stream導到video tag去播放(理應就這樣做), 但這樣出來的比例是camera的比例, Instagram的照片都是方形的, 要實現這點, 其實也不難, 就另外把內容畫到另一個方形canvas, 在video play的時候開始每隔40ms畫一次, 把video畫到canvas的方式也不難, 就把他當image看待就行了
不過當第一次使用時, 瀏覽器會跳出詢問是否允許使用相機的對話窗:
因為偷懶, 拍照的部份沒沿用原本的canvas, 另起一個Canvas, 並把可用的濾鏡放在下面:
接下來就是神奇的CamanJS的工作了, CamanJS是一個以CoffeeScript實作出來的影像處理的lib, 還真的蠻厲害的, 害我都有點想研究一下CoffeeScript了
使用方式非常的簡單, 像以下面的程式:
Caman("path/to/image.jpg", "#canvas-id", function () {
// manipulate image here
this.brightness(5).render();
});
就可以提高影像的亮度了,
此外還有許多預設的濾鏡, 我也偷懶直接採用, 這就是做出來的效果:
Source codes分享於此: https://github.com/julianshen/instagramlikecam
References:
2012年5月7日 星期一
[筆記] Self published B2G app
B2G並未一定要開發者把應用程式發表到一個特別的app store, 網站也可以自行加一個install按鍵, 讓使用者把你的網站當應用程式裝到手機內
首先, 你必須要先有個manifest, 目前manifest並沒強制的檔名, 但文件裡建議叫 xxxx.webapp, 以下是範例內容:
{ "name": "MyTestApp", "description": "test app", "launch_path": "/", "icons": { "128": "/img/icon.png" }, "developer": { "name": "Julian Shen" } }
然後在網頁內加上檢查是否安裝, 以及安裝的程式碼, 範例如下:
var manifestUrl = 'http://localhost:3000/manifest.webapp'; var app = navigator.mozApps; function checkInstalled() { var request = app.getSelf(); request.onsuccess = function() { if(request.result) { //installed console.log('installed'); document.getElementById('installmsg').style.display='none'; } else { //not installed console.log('not installed'); } }; } function install(cb) { var request = app.install(manifestUrl); request.onsuccess = function() { alert('installed'); }; request. function() { alert('Not installed'); } }
這邊要注意的是manifestUrl必須是full path而不是只有相對位置, 寫相對位置, 它還是讀的到, 但會出parse error, 之前害我搞半天一直以為是格式問題, trace了一下Webapps.js, 發現可能是判斷install origin有問題
目前安裝介面很醜:
安裝完就可以在Home screen看到它的存在了:
2012年4月16日 星期一
[筆記] Deploy Mojito app to Heroku
First, you need to follow this doc (http://developer.yahoo.com/cocktails/mojito/docs/quickstart/) to install Mojito as a global node module. Why install it globally? Because you need the Mojito command line to create your first app.
Create application with command line:
mojito create app mymojito
Change working directory to "mymojito".
Create a "Procfile" whose content is:
web: mojito start $PORT
Use the following command to create an application on heroku and deploy codes to it.
- git init
- git add .
- git commit -am init
- heroku create mymojitotest --stack cedar
- git push heroku master
After successfully commit codes to heroku, you could check running process with "heroku ps". However, you cannot find any running process. That is because there is one important step missed:
heroku ps:scale web=1
This command adds one dyno for web process. After that, you will find one web process when you check with "heroku ps". And the server is now started up. You can check your app with browser.
2012年4月12日 星期四
[筆記] Practice: Open graph with Facebook JavaScript API
Source: https://bitbucket.org/julianshen/videomag/overview
Demo URL: http://growing-wind-8625.herokuapp.com/top
Server: Play framework 2.0 on Heroku
Open graph object: Youtube video, Action: watch
It's not a problem to add youtube video since it already contains open graph meta data in page. All we have to do is call "/me/video.watches?video=youtube_link" with POST.
To post activity to timeline, the permission "publish_actions" is required.
The result on timeline: