2010年9月6日 星期一

NCC 自用切結書

幾個禮拜前,訂購新款的 Amazon Kindle 3 閱讀器,其全名:Kindle Wireless Reading Device, Wi-Fi, 6" Display, Graphite - Latest Generation,這是只有 Wi-Fi 功能版的,下完單後,緊接著接到電話通知,說要準備好"射頻"跟"功率"資料,這時就有點大囧。


偷懶不尋求正規方式,直接 Google 的下場,就只是看到一堆抱怨文章,像是東西被扣在機場海關等等的訊息,完全是沒幫助又浪費時間。於是,探問前輩得知美國那邊也有類似的機構,稱作 Federal Communications Commission (FCC),因此,就筆記一下找資料的過程。



  1. 首先,必須找到購買機型的一些資訊,如 FCC ID 等,這部份就只好上官網找找,也就是 Kindle Wireless Reading Device, Wi-Fi, 6" Display, Graphite - Latest Generation,其下方有個 Kindle User's Guide,裡頭會跟你說 FCC ID 是多少,你可以用 Search 的方式

  2. 接著,可以只用 "FCC id search" 三個關鍵字去問 Google,他會告訴你 OET -- FCC ID Search,就是用來查詢機器資訊的

  3. 然而,這台 Kindle 竟然有兩個型號!XSX-1013 和 X7N-0610 啊,那又該怎樣確定呢?用法就是兩個型號都查一下,最後在 [Display Exhibits] -> [Detail] -> [External Photos] 可以看看他外觀那個是你要得,慶幸的 Wi-Fi Only 是黑色的 XD 所以就能確認是 XSX-1013,接著可以在 [Display Grant] 得知 Frequency Range (MHZ) 和 Output Watts 資訊

  4. 最後,要填寫的單子比較要找資料的項目:

    • 器材名稱:  KINDLE WIRELESS READING DEVICE

    • 廠牌:  AMAZON KINDLE

    • 型號:  D00901 (說明書有寫 Model Number)

    • 工作頻率:  Wi-Fi, 2412-2462 MHz(有人會直接寫 Wi-Fi 、3G Wireless 的方式,我是寫兩個試試)

    • 輸出功率:   0.155 W




最後稍微提一下 FCC ID Search 的那個表單,Grantee Code 和 Product Code,前者是三碼,後者則是有包括 "-" 喔,也就是在這個例子要輸入 "-1013" 才能查到,當初光這個就誤了我大半時間,最後跑去問老闆,雖然一開始也是一樣卡在那邊,但 30 秒後老闆馬上點了 Product Code 的敘述資訊,就看到輸入規則有包含 "-" 了 Orz 這就是有經驗跟沒經驗的差別啊。


2010年9月3日 星期五

iOS 開發教學 - 使用 UIWebView 之 Javascript 呼叫 Objective C & 實做簡單的 Web Server?

幾個月前學習使用 UIWebView 時,就是作一個簡單得 Browser 出來,提供輸入 URL 以及上下頁等等基本功能,然後在這樣得過程中,在切換上下頁時,又想要更新 URL 欄位資訊,此時就是替 UIViewController 加上 UIWebViewDelegate 並使用 - (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType 解決。


已經有一陣子跟同事討論有沒有可能用 Javascript 去呼叫 Objective-C 做的 function 呢?雖然一直反反覆覆地回他,但今天認真看一下,找到這篇 Calling Objective-C from JavaScript in an iPhone UIWebView ,原來,他用到的技巧就是之前學作一個簡單 Browser 會用到的,去偵測點到的 hyperlink!因此,換個方式以 Javascript 使用 window.location = 'call?HelloWorld'; 時,透過 UIViewController + UIWebViewDelegate 攔截到 url 後,就可以透過 pattern matching 來決定啟用自己內定的 Objective-C function 啊!這麼簡單就解決了,果真學東西要融會貫通才有用!

下一步的思考:那我可不可以寫個簡單的 web server 擺在 iPhone native app 咧?這個答案是有可能的!並且可以考慮使用 Ajax !

現在從 Javascript 可以呼叫 Objective-C 後,而 Objetive-C 本身就可以呼叫 Javascript 啦,那就只需要從 Javascript 呼叫 Objective-C 時,準備好 callback function,之後在 Objective-C 處理完後,可以透過控制 UIWebView 去呼叫 Javascript 囉!

關於 Ajax Query 部份:

經過測試,使用 Ajax 並不能被偵測出來,只能用那種真的有在換頁的效果的,如 <a href="http://www.google.com.tw">Google</a> 這種方式,或是原先的 window.location 囉!

程式碼:

@ UIWebViewController.h (直接建立一個 UIViewController 並新增以下資料即可)

#import <UIKit/UIKit.h>
@interface UIWebViewController : UIViewController<UIWebViewDelegate> {
}
@end

@ UIWebViewController.m (直接建立一個 UIViewController 並新增以下資料即可)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
        NSString *target = [[request URL] absoluteString];
        NSRange check = [target rangeOfString:@"MyCGI?"];
        //
        //  用來判斷是否有進入這個 function
        //
        NSLog(@"Catch:%@",target);
        if( check.location != NSNotFound )
        {
                //
               
// 這邊就是自己的 CGI 要做的事,包含處理 request 以及呼叫 Javascript callback function!
               
//
                [webView stringByEvaluatingJavaScriptFromString:
                        [NSString stringWithFormat:@"report('[MyCGI Report] %@');", [target substringFromIndex:check.location]]];

                return NO;
        }
        return YES;
}

- (void)viewDidLoad {
    [super viewDidLoad];

        UIWebView *myWebView = [[UIWebView alloc] init];
        [myWebView setFrame:self.view.frame];
        [self.view addSubview:myWebView];

        [myWebView
                loadData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]]
                MIMEType:@"text/html"
                textEncodingName:nil
                baseURL:nil];
        [myWebView setDelegate:self];
        [myWebView release];

}

@ YourAppDelegate

#import "UIWebViewController.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.
        UIWebViewController *webViewCtrl = [[UIWebViewController alloc] init];
        [window addSubview:webViewCtrl.view];
        //
        // 在此偷懶不 release 他(不然無法正確偵測)!正規寫法應該要在 header 宣告,並且在 dealloc 進行 release
        //

    [window makeKeyAndVisible];

        return YES;
}

@ index.html (別忘了擺這個檔案進去 app 囉)

<html>
        <body>
                <script language="Javascript">
                        function report( s , clear )
                        {
                                var report;
                                if( ( report  = document.getElementById( 'report' ) ) )
                                {
                                        while( clear && report.hasChildNodes() && report.childNodes.length >= 1 )
                                                report.removeChild( report.firstChild );
                                        if ( s )
                                                report.innerHTML = s + '<br/>';
                                }
                        }
                        function doQuery( target )
                        {
                                report( null , true );
                                try
                                {
                                        var ajReq = new XMLHttpRequest();
                                        ajReq.open( 'GET' , target , true );
                                        ajReq.send(null);
                                        report( 'Query: ' + target );
                                }
                                catch(err)
                                {
                                        report( '[Error] ' + err , true );
                                }
                        }
                        function doLQuery( target )
                        {
                                report( null , true );
                                report( 'LQuery: ' + target );
                                window.location = target;
                        }
                </script>
                <div id="report"></div>
                <button onclick="doQuery('http://www.google.com.tw');">Ajax Query - Google</button><br />
                <button onclick="doQuery('http://localhost/MyCGI?HelloWorld');">Ajax Query - MyCGI</button><br />
                <button onclick="doLQuery('http://www.google.com.tw');">location Query - Google</button><br />
                <button onclick="doLQuery('http://localhost/MyCGI?HelloWorld');">location Query - MyCGI</button><br />
                <a href="http://www.google.com.tw" target="_blank">Google.com</a>
                <a href="http://localhost/MyCGI?HelloWorld" target="_blank">MyCGI</a>
        </body>
</html>

參考資料:


2010年9月2日 星期四

關閉文章留言

這幾個禮拜,每天都會收到幾個不等的廣告留言,有會員也有非會員,但非會員的留言相較起來多很多,所以先只允許會員迴響一陣子看看。真的想留言問問題,就...註冊吧,不過這邊本來就是自己的筆記為主,鮮少互動就是了 :P


iPod Touch 4!








今天已經看到許多關於 Apple 的新聞,有一項是 iPod Touch 4 的亮相!此 iPod Touch 4 跟以往不一樣,跟 iPhone 4 的差距只剩下電話與 3G 網路的差別,也就是說 iPod Touch 4 已經有 Camera ,並且跟 iPhone 4 一樣是有前後兩種。仔細回顧一些細節,我覺得對 iOS app 開發者而言,跨入的門檻越來越低囉!只需要 iPod Touch 4 + Mac mini + 開發者年費,最低額台幣僅 8960(229美金) + 19900(學生價) + 3200(99美金) !


偶爾留意一下台灣 iPhone App 的排行榜,除了遊戲容易上榜外,看得出來目前一些工具以 LBS 最吸引人,像是即時的台北公車時間、停車場空位、火車高鐵查詢訂位等,甚至還有女生的好朋友記錄軟體以及一堆跟算命、拍照相關的軟體。


覺得 iPhone 對最吸引人我的是 LBS 服務和拍照。隨時隨地拿起來就拍一張,雖然還不及一些低階的數位相機,但其方便性已經超越低階相機囉!所以 iPod Touch 4 可以拍照後,再加上它的低價還滿令人心動的。只是不知道是不是 iPhone 4 硬體跟 iPod Touch 4 有差?不然光能打個電話跟 3G 上網,價差就超過一萬!我承認 3G 上網的價值才是 Mobile Device 的最大特點,但沒想到價差還不少,或是該說 iPhone 跟電信業者合作包含兩年(通常綁約兩年)的 3G 無線上網的費用呢?若是這樣就還稱得上划算吧。


早上跟高中同學閒聊,他說他沒空花時間去開發程式,時間就是金錢,以這句話來說,他也是變相在賺錢啊 :D 所以,仔細想想,凡事還是要好好規劃,不要一窩蜂地只做賺錢的事而已,該衡量對自己真正的價值在哪才行動,我很佩服那位高中同學很花心思地照顧小孩,也該播個時間去拜訪囉!至於 iOS app 的市場,我覺得漸漸會縮到幾間大廠或幾個特定項目的服務吧!如同前陣子看到國外新聞在講 iOS app 已經有二十多萬個,能找到自己需要的程式嗎?轉個角度來說,會不會漸漸地跟目前現實生活的生態一樣,就只挑名牌廠商做的?而新興的小程式又會有誰看到呢?只是這樣的生態還要幾年才會穩定,目前的開發者還是有機會成為百萬富翁啦!


相關文章:



2010年8月31日 星期二

Javascript Unzip Testing

前陣子一直在調校 Javascript & Unzip 的事情,找到一個滿貼切工作的 -- Booktorious ,並且開始修改它。只是再怎樣地修改,在 Mobile Device 都不太適用,也被提醒會不會挑到的程式沒有實做很好,這部份我有留意它 unzip 的部份,的確存在不少可以精進的地方,當我準備要改得時候,我又看到了 rePublish 裡頭用的 zip 其實就已經接近我要改善的方式,因此筆記一下比較的過程,之後有空再慢慢增加其他對應的 library。


整個過程,看起來有點線性的成長,隨著檔案大小的增加,解壓縮的時間也會接近倍數成長。而 Booktorious 之 js-unzip 裡頭,有用到大量的資料複製,所以時間上花費會更多,相對於 rePublish 之 zip 的使用,對於 raw data 採用紀錄 offset 的方式,因此比 Booktorious 更加接近線性關係。


函式庫:



測資(純粹看 size 關係而非內容或檔案數目):



@ AMD X4 955, Ubuntu 10.04 i386, DDR3-1333 4GB, Google Chrome 6.0.495.0 dev


91KB


0.034s @ [js-zip & js-inflate]
0.026s @ [zip & inflate]


929KB


0.302s @ [js-zip & js-inflate]
0.139s @ [zip & inflate]


9.2MB


14.945s @ [js-zip & js-inflate]
1.654s @ [zip & inflate]


@ iPad, iOS 3.2.2


91KB


2.182s @ [js-zip & js-inflate]

1.214s @ [zip & inflate]


929KB


JavaScript execution exceeded timeout @ [js-zip & js-inflate]

7.177s @ [zip & inflate]


9.2MB


JavaScript execution exceeded timeout @ [js-zip & js-inflate]
JavaScript execution exceeded timeout @ [zip & inflate]


@ iPhone 3G, iOS 4.0.2


91KB


8.438s @ [js-zip & js-inflate]
5.397s @ [zip & inflate]


929KB


JavaScript execution exceeded timeout @ [js-zip & js-inflate]
JavaScript execution exceeded timeout @ [zip & inflate]


9.2MB


JavaScript execution exceeded timeout @ [js-zip & js-inflate]
JavaScript execution exceeded timeout @ [zip & inflate]


以下是實驗的 Source Code,而測試中如果瀏覽器已經等很久甚至產生 timeout 的訊息時,試著一次只測試一個 library 吧,並且在 iPad 或 iPhone 也有機會碰到直接跳出 Safari 的情況


@index.html


<!DOCTYPE html>
<html xml:lang="utf-8" lang="utf-8" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script type="text/javascript" src="js-unzip.js"></script>
        <script type="text/javascript" src="js-inflate.js"></script>
        <script type="text/javascript" src="zip.js"></script>
        <script type="text/javascript" src="inflate.js"></script>
    </head>
    <body>
<script language="Javascript">
    function report( s, clear )
    {
        var report = document.getElementById( 'report' );
        if( clear )
            while ( report.hasChildNodes() && report.childNodes.length >= 1 )
                report.removeChild( report.firstChild );
        if( s )
        {
            report.appendChild( document.createTextNode( s ) );
            report.appendChild( document.createElement( 'br' ) );
        }
    }
    function doZip( epub , choose )
    {
        var path = epub || "epub/91kb.epub";
        var ajReq = new XMLHttpRequest();
 
        try{
            ajReq.open( 'GET' , path , false );
            ajReq.overrideMimeType( 'text/plain; charset=x-user-defined' );
            ajReq.send(null);
            ajReq.overrideMimeType( 'text/plain; charset=UTF-8' );
 
            var out = ajReq.responseText || '' ;
            var out_array = [];
            for( var i=0, len=out.length, scc=String.fromCharCode ; i<len ; ++i )
                out_array[i] = scc( out.charCodeAt(i) & 0xff );
            var out_binary = out_array.join( '' );
 
            if( out_binary !== '' )
            {
                var cost ;
                var unzipper;
 
                report( null, true );
 
                //
                // js-unzip & js-inflate
                //
                if( !choose || choose === 1 )
                {
                    unzipper = null;
                    cost = new Date();
                    unzipper = new JSUnzip( out_binary );
                    if( unzipper.isZipFile() )
                    {
                        unzipper.readEntries();
                        for (var i = 0 , len = unzipper.entries.length; i < len ; i++)
                        {
                            if ( unzipper.entries[i].compressionMethod === 0)
                                ; // unzipper.entries[i].data
                            else if ( unzipper.entries[i].compressionMethod === 8)
                                JSInflate.inflate( unzipper.entries[i].data );
                        }
                    }
                    cost = new Date() - cost ;
                    report( (cost / 1000.0) + 's' + ' @ [js-zip & js-inflate]' );
                }
 
                //
                // zip & inflate
                //
                if( !choose || choose === 2 )
                {
                    unzipper = null;
                    cost = new Date();
                    unzipper = Zip;
                    unzipper.Archive( out_binary );
                    for (var i = 0 , len = unzipper.entries.length; i < len ; i++)
                        unzipper.entries[i].content();
                    cost = new Date() - cost ;
                    report( (cost / 1000.0) + 's' + ' @ [zip & inflate]' );
                }
            }            
 
        }catch( err ){
 
            alert( 'Error:' + err );
 
        }
    }
</script>
        <dl>
            <dt>Do all</dt>
            <dd><button onclick="doZip( 'epub/91kb.epub' );">Unzip 91KB</button></dd>
            <dd><button onclick="doZip( 'epub/929kb.epub' );">Unzip 929kB</button></dd>
            <dd><button onclick="doZip( 'epub/9.2mb.epub' );">Unzip 9.2MB</button></dd>
        </dl>
        <dl>
            <dt>Use Booktorious</dt>
            <dd><button onclick="doZip( 'epub/91kb.epub' , 1 );">Unzip 91KB</button></dd>
            <dd><button onclick="doZip( 'epub/929kb.epub' , 1 );">Unzip 929kB</button></dd>
            <dd><button onclick="doZip( 'epub/9.2mb.epub' , 1 );">Unzip 9.2MB</button></dd>
        </dl>
                    
        <dl>
            <dt>Use rePublish</dt>
            <dd><button onclick="doZip( 'epub/91kb.epub' , 2 );">Unzip 91KB</button></dd>
            <dd><button onclick="doZip( 'epub/929kb.epub' , 2 );">Unzip 929kB</button></dd>
            <dd><button onclick="doZip( 'epub/9.2mb.epub' , 2 );">Unzip 9.2MB</button></dd>
        </dl>
        <p id="report"></p>
    </body>
</html>