星期四, 6月 14, 2012

在 Windows 環境使用 Air 3.3 的 platformsdk 參數


在之前有介紹過 "使用 Air Native Extension(ANE) 在 Air Mobile App" 其中最後要產出 IPA 時必須在 Mac 電腦上執行
AIR 3.3 已經可以在 Windows 環境使用 -platformsdk 來包 iPhone 的 SDK 了
基本上步驟完全一樣 比較麻煩的是要取得 iPhoneOS 的 SDK
這篇有教如何從 Mac 抓出 SDK ((如果沒 Mac 環境 下方討論有人放了一份 SDK 快去抓吧))

一樣用之前的原生 Google Map ANE 當範例, 首先先到 Mac 把 iPhone SDK 拷貝出來 做法
  1. 開啟 Mac 終端機 切到放 iPhone SDK 目錄
  2. 指令 zip -r ./iPhoneOS43sdk.zip ./iPhoneOS4.3.sdk
  3. 到 Windows 把 iPhoneOS43sdk.zip 解壓縮 

接下來的步驟都一樣 放 ANE, 設定 ANE ID 到 application.xml, 寫 CODE 產出 IPA
public class MapAneTest extends Sprite {
	private var _nativeGoogleMap:Map;
	private var _btn:Sprite = new Sprite();
	private var _mType:int = 0;
	
	public function MapAneTest() {
		_btn.graphics.beginFill(0xDD0000);
		_btn.graphics.drawRect(0, 0, 100, 70);
		_btn.graphics.endFill();
		_btn.x = (stage.stageWidth >> 1) - 50;
		_btn.y = 15;
		addChild(_btn);
		_btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
			(_mType == 2) ? _mType = 0 : _mType++;
			_nativeGoogleMap.setMapType(_mType);
		});
		
		var mapVP:Rectangle = new Rectangle(0, 100, 320, 360);
		_nativeGoogleMap = new Map();
		_nativeGoogleMap.viewPort = mapVP;			
		_nativeGoogleMap.setSize(new Point(320, 360));
		_nativeGoogleMap.setMapType(_mType);
		_nativeGoogleMap.setZoom(15);
		_nativeGoogleMap.setCenter(new LatLng(25.0361, 121.5686));
		_nativeGoogleMap.visible = true;
		
		var marker:Marker = new Marker(new LatLng(25.0361, 121.5686));
		marker.title = "AIR 3.3 -platformsdk test";
		marker.subtitle = "Use -platformsdk on Windows~";
		_nativeGoogleMap.addOverlay(marker);
	}
}


一樣最後加上 -platformsdk [iPhone sdk 目錄位置]

一樣會出現一些 warning... 不理他 安裝 IPA 到手機試試看

OK~ 一樣可以用~


星期四, 5月 31, 2012

Air 3.3 使用 USB 對 iOS 程式除錯


Adobe Air SDK 3.3 Beta 釋出了一些 新的功能 測了一下 USB debugging for AIR iOS 這部分
首先當然是先把 3.3 Beta SDK & Air Player 裝一裝 我測試的程式如下

在發布時選擇發布成 ipa-debug-interpreter 或 ipa-debug 都可以
然後再加上 -listen [連接埠] 長的會像這樣
adt -package -target ipa-debug-interpreter -listen [連接埠] ....
連接埠如果不填 預設是 7936 ((之後會用到))
然後產出 ipa 檔後安裝進 iPhone 並啟動應用程式 應該會看到如下畫面

這時 開始->執行->cmd 跳出MS-DOS命令提示字元視窗 然後切換到放置 3.3 SDK 的目錄下
進到 lib\aot\idb 目錄裡 然後執行 idb -devices 應該會看到如下圖

然後輸入
idb -forward 8765 7936 2
8765 是本機連接埠 亂填一個數字即可 官方建議8000以上
7936 就是剛產生 ipa 時 -listen 的連接埠
2 是裝置的編號

這邊完成後 就切換到3.3 SDK 的目錄下的 bin 目錄 然後執行 fdb -p 8765 開啟 fdb 除錯功能
接下來在 (fdb) 提示列下輸入 r ((開始讀取除錯)) 應該就會看到如下圖

這時就可以開始除錯囉 fdb 除錯說明可以參考 http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-7ffb.html
這邊測一下設斷點跟打印變數值
首先先設定斷點 語法是
(fdb) break 44
會出現
中斷點 1: 檔案 RTMPPlayer.as,行 44
然後開始執行
(fdb) cont
當進入斷點時畫面會出現
中斷點 1,位於 RTMPPlayer.as:44 的 onClickStage()
 44                     trace("_currentTime=" + _currentTime);
這時可以執行其他操作 EX打印變數值
(fdb) print _currentTime
會出現
$1 = 0 (0x0)

其他 fdb 的操作就得參考官方說明 我不是很熟... 因為我都是用 FlashDevelop 裡面的 Start Remote Session 來除錯, 但目前找不到方式 讓 FlashDevelop 的除錯去監聽特定的連接埠(7936)...((不知道是不是跟參考資料中 說明最後面有寫到 Currently this feature is not supported on Flash Builder.))

USB 來除錯看起來似乎沒有用原本 WiFi 連結方式除錯方便...
如果都在一個區網裡 建議還是用 WiFi 簡單多了 而且還可以用 FlashDevelop 的除錯方便很多

參考資料 : http://labsdownload.adobe.com/pub/labs/flashplatformruntimes/shared/air3-3_flashplayer11-3_p3_releasenotes.pdf

星期五, 5月 25, 2012

First Air Mobile App 上架


最近的專案 手機部分 App 都上架了

Android 沒啥意外的順利上架, 硬要說就是付那US$25元時, Google 要求信用卡持有人身分證明卡了一下, 把 Google 要的資料(我們是附上身份證與信用卡帳單封面)一天後 Google Wallet 就開通了(不過又多等了一天 Google確認帳款有請到), 接下來就順利把 App 給放上去, 約莫一個小時後 Google Play 就可以搜尋下載了.

iOS 的第一次送審結果被說違反 20.1 & 20.2 退件(送出到退回大概經過8天)... 再送一次補了客戶的授權證明(針對 20.1) 跟 活動聲明(針對 20.2) 終於過關了(也大概是8天)~

這是第一個手機 App 也是第一個用 Air 開發的, 因為希望達到 60FPS 操作體驗, 所以就採用了 Starling Framework, 以下大概是有用到的功能或API:

  1. Adobe Air SDK 3.2
  2. Starling Framework 1.0
  3. Accelerometer
  4. Facebook Mobile API
  5. ANE - iNativeAlert : 跳出原生提示訊息用
  6. Camera
  7. CameraRoll(僅iOS)
  8. OSMF MediaPlayer : 用來播放影片的
  9. StageWebViewBridge : 與頁面 JavaScript 溝通,主要用途是 Google Analytics

因為 Starling Framework 所以在大部分的手機上 都可以跑到 60FPS, 但也因為 Starling 有些內建的功能沒法用或有問題,
1. CameraUI 沒法用 : 當然如果把 Starling 的 handleLostContext 設 true 就可以用, 但這樣貼圖記憶體使用就會大增(好像是多1倍) 如果貼圖多的 APP 可能很快就掛了
2. CameraRoll 在 Android 上每點必當 : 每次呼叫 browseForImage() Android 手機幾乎都跳出 App, 曾經在三爽的S2成功過, 但後來還是一直當, 所以 Android 版就把 CameraRoll 拿掉了

另外 使用 Facebook Mobile API 也遇到一些問題, 主要的問題就是""...FB回應真的挺慢的... 還有 FB 的登入視窗跳出來後 預設文字是印尼文.... 這很奇怪, 因為開發時都沒這問題, 且也不是每次都會遇到, 直到要送審前才忽然變這樣... 不知道是不是 FB 改了判別語系的方式, 沒辦法... 只好在登入的 URL 動手腳, 登入的 URL 後面硬加上語系 &locale=zh_TW 就好了, 只是還是會先看到一下印尼文才跳到繁中畫面~ *(這個 Bug Facebook 已經修正了 https://developers.facebook.com/bugs/407246299295529?browse=search_4fab73cda08dd0f21498385 )

其他好像就還好沒啥太大問題, Air 開發好處就是可以無痛發布 iOS & Android 的 App, 基本上我們準備了寬度 320,480,540,640px 四種尺寸的貼圖檔, 大體上發佈出來都很OK, iPhone Android 都可以順利執行, App 下載位置在

Android : https://play.google.com/store/apps/details?id=air.com.media_palette.reddotplan
iOS : http://itunes.apple.com/tw/app/hong-dian-ji-hua/id524902106?l=zh&mt=8

可以下載玩玩看, 說不定還會不小心抽中 Honda CIVIC 一台~


星期二, 5月 22, 2012

Adobe Air Mobile App 送 iOS 審核


前陣子把 Air App 送 iOS 審核, 流程記錄一下,
itunes connect 部份的前置工作就不多說了
反正就是處理到狀態變成 "Waiting For Upload" 就可以打開 Mac 的 Application Loader 準備上傳了*
因為上傳僅接受 ZIP 檔, 所以我的做法是

1. 把 XXXX.ipa 複製到 Mac 裡
2. 在 Mac 把 XXXX.ipa 更名為 XXXX.zip
3. 然後解壓縮出 Payload 目錄
4. 點進 Payload 目錄應該會看到 XXXX.app 然後壓縮這檔案(目錄)為 XXXX.zip
5. 用 Application Loader 上傳這 ZIP 吧~ 完成~等審核~

P.S.
Mac 必須有安裝 Xcode, 如果直接用 Application Loader(itunes connect裡面下載的) 去傳會發生錯誤, 在 Snow Leopard 先裝了 Xcode 4.2 for Snow Leopard 再用裡面的 Application Loader 去上傳 App 就可以了

另外 我在 Lion 上也傳過 不過因為 Xcode 4.3.2 for Lion 是一個 App 的型態, 我找不到內附的 Application Loader... 所以跑去itunes connect裡面下載 然後上傳也沒問題


星期四, 4月 26, 2012

使用 Air Native Extension(ANE) 在 Air Mobile App


Air SDK 好像是到了 3.0 之後的版本都可以使用 Air Native Extension(ANE)
網路上有些神人們寫好且免費的 ANE 可以使用 以 iOS 來說就用過 iNativeAlert, AdMob iOS, Vibration, NetworkInfo 等
可以到 http://www.adobe.com/devnet/air/native-extensions-for-air.html 或是 http://extensionsforair.com/ 找找

紀錄一下怎麼使用 ANE 以 iOS Maps (http://code.google.com/p/air-maps-ane/) 當範例
1. 下載 ANE 主要要有 2 個檔 airmaps.ane & iMaps.swc
2. airmaps.ane 就在專案下開個目錄 ext 放到裡面吧
3. 在專案中把 iMaps.swc 加到元件庫 並設定外部元件庫 (就是編譯不包進去)

4. 設定 ANE 進 application.xml 裡
<extensions>
 <extensionID>com.sampleNativeExtensions.Maps</extensionID>
</extensions>

5. 寫 CODE
private var googleMap:Map;  
public function iMapTest() {   
 var sp:Sprite = new Sprite();
 sp.graphics.beginFill(0xDD0000);
 sp.graphics.drawCircle(0, 0, 40);
 sp.graphics.endFill();
 addChild(sp);
 sp.x = stage.stageWidth >> 1;
 sp.y = 40;
 
 var viewPortMap:Rectangle = new Rectangle(0, 80, 320, 400);
 googleMap = new Map();
 googleMap.viewPort = viewPortMap;
 
 googleMap.setSize(new Point(320, 400));
 googleMap.setMapType(0);
 googleMap.setZoom(17);  
 googleMap.setCenter(new LatLng(25.18446, 121.7515));
 googleMap.visible = true;
 
 var marker:Marker = new Marker(new LatLng(25.18446, 121.7515));
 marker.title = "TEST";
 marker.subtitle = "AWESOME!!";
 googleMap.addOverlay(marker);
}

6. Build 專案 產出 SWF 檔 ((有用ANE好像都沒法在 Air Player 上先試跑...))
7. 編譯 IPA 檔 這邊比較特別的是 因為使用 ANE 原本的編譯指令後面要加上 -extdir 放置 .ane 檔目錄 一般情況的 ANE 應該這樣就可以產出 IPA 檔 不過 iOS Maps 這個 ANE 有用到 iOS SDK 的 MapKit 所以得再加上 -platformsdk iOS SDK 的路徑 也就是說如果要用這 ANE 必須在 Mac 電腦下執行... 在 Mac 下開啟終端機 輸入

adt 
-package
-target ipa-{看要包哪種形式}
-storetype pkcs12
-provisioning-profile {MOBILE_PROVISION 檔案位置}
-keystore {P12 檔案位置}
-storepass {P12 檔密碼}
iOSNativeMaps.ipa
application.xml
bin/iOSNativeMaps.swf
-extdir ext
-platformsdk /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk

很奇怪 我生成時有 warning 不過 IPA 還是產生出來了


8. 安裝到手機上看看 OK出來了~


使用原生的地圖效能真的比較好 不過這個 ANE 目前提供的 API 還不是很多 應該還是夠用... 如果嫌這 ANE 功能不足的話 就還是得用 StageWebView 來解決 然後再搭配 StageWebViewBridge 功能會更齊全

星期一, 4月 23, 2012

增加取消登入 FB 在 Adobe Air Mobile 發生錯誤


在Air上面使用 FB Mobile Api遇到一個問題...
就是預設的 FB 登入畫面並沒有取消登入的按鈕 所以就自己加了一顆按鈕在 FB 登入視窗的 StageWebView 外面(右上角X)
本想要讓 USER 點了按鈕後可以將 StageWebView 給拿掉 不管是改變 viewport 或是直接 dispose
所以一開始就先宣告了一個 StageWebView 使用這個 StageWebView 來處理有關 FB 的部分
private var _swv:StageWebView;

當點了取消登入 就去
_swv.viewport=new Rectangle();

不過有時要做這動作時 都會發生錯誤 錯誤原因是原本給 FB 用的 StageWebView 已經變成 null 當然就沒法進行操作

看了一下 當登入成功或失敗 在 MobileLoginWindow.as 的 handleLocationChange() 會將原本的 StageWebView 給 dispose
所以這時就不能再對餵給 FB 的 StageWebView 進行操作 看起來似乎沒有問題 不過 如果這時 USER 執行登出 並再次登入
因為原本的 _swv 已經被 dispose 所以當要再重新指定 viewport 或餵給 FB 就會發生錯誤

所以改了一下 FB 的 API

MobileLoginWindow.as
protected function handleLocationChange(event:Event):void
{
 var location:String = webView.location;
 if (location.indexOf(FacebookURLDefaults.LOGIN_FAIL_URL) == 0 || location.indexOf(FacebookURLDefaults.LOGIN_FAIL_SECUREURL) == 0)
 {
  webView.removeEventListener(Event.COMPLETE, handleLocationChange);
  webView.removeEventListener(LocationChangeEvent.LOCATION_CHANGE, handleLocationChange);
  loginCallback(null, FacebookDataUtils.getURLVariables(location).error_reason);
  userClosedWindow =  false;
  //webView.dispose();
  //webView=null;
 }

 else if (location.indexOf(FacebookURLDefaults.LOGIN_SUCCESS_URL) == 0 || location.indexOf(FacebookURLDefaults.LOGIN_SUCCESS_SECUREURL) == 0)
 {
  webView.removeEventListener(Event.COMPLETE, handleLocationChange);
  webView.removeEventListener(LocationChangeEvent.LOCATION_CHANGE, handleLocationChange);
  loginCallback(FacebookDataUtils.getURLVariables(location), null);
  
  userClosedWindow =  false;
  //webView.dispose();
  //webView=null;
 }
}

另外在 FacebookMobile.as 的 login() 不知WHY要每次呼叫登入都會產生一個新的 MobileLoginWindow
((可能是 FB 認為每次 StageWebView 都是全新的))
protected function login(callback:Function, stageRef:Stage, extendedPermissions:Array, webView:StageWebView = null, display:String = 'touch'):void {
 this.loginCallback = callback;
 this.stageRef = stageRef;
 if (!webView) {
  this.webView = this.createWebView();
 } else {
  this.webView = webView;
  this.webView.stage = this.stageRef;
 }
 this.webView.assignFocus();
 if (applicationId == null) {
  throw new Error(
   'FacebookMobile.init() needs to be called first.'
  );
 }
 //開過了就不再多開了
 if (loginWindow == null) {
  loginWindow = new MobileLoginWindow(handleLogin);
  loginWindow.open(this.applicationId,
   this.webView,
   FacebookDataUtils.flattenArray(extendedPermissions),
   display
  );
 }else {
  loginWindow.directShowWindow();
 }   
}

還有
protected function handleLogin(result:Object, fail:Object):void {
 //loginWindow.loginCallback = null; //因為不重複開LOGINWINDOW所以不清掉CALLBACK
....


回到 MobileLoginWindow.as 加上
public function directShowWindow():void {  
 showWindow(loginRequest);
}


這樣就 OK 了~
之後要關閉 FB 登入視窗 就直接 _swv.viewport=new Rectangle();
記得原本登入成功或失敗 都會去 call 的 loginCallback 自己加上 _swv 的後續處理
不太確定這方法對不對 還是有誤會 FB 原本的用法 但至少目前用起來還蠻 OK 的~

星期三, 4月 18, 2012

Sprite Sheet Packer

網路上找到的用 C# 寫的 Open Source 的 Sprite Sheet 產生軟體 http://spritesheetpacker.codeplex.com/
蠻簡單好用的 不過因為我都是要倒給 Starling 的 TextureAltas 用的 所以稍微改一下他的原始碼
在 \sspack\Exporters\TxtMapExporter.cs 檔
public string MapExtension
{
 get { return "xml"; }
}

public void Save(string filename, Dictionary<string, Rectangle> map)
{
 // copy the files list and sort alphabetically
 string[] keys = new string[map.Count];
 map.Keys.CopyTo(keys, 0);
 List<string> outputFiles = new List<string>(keys);
 outputFiles.Sort();

 using (StreamWriter writer = new StreamWriter(filename))
 {
  writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  writer.WriteLine("<TextureAtlas imagePath=\"" + Path.GetFileNameWithoutExtension(filename) + ".png\">");
  foreach (var image in outputFiles) {
   // get the destination rectangle
   Rectangle destination = map[image];

   // write out the destination rectangle for this bitmap
   writer.WriteLine(string.Format(
    "   <SubTexture name=\"{0}\" x=\"{1}\" y=\"{2}\" width=\"{3}\" height=\"{4}\" />", 
    Path.GetFileNameWithoutExtension(image), 
    destination.X, 
    destination.Y, 
    destination.Width, 
    destination.Height));
  }
  writer.WriteLine("</TextureAtlas>");
 }
}
有改的都標記出來了, 再重新編譯產出 sspack.exe 檔覆蓋掉原本的即可, 產出 XML 的看起來就會像是
<?xml version="1.0" encoding="UTF-8"?>
<TextureAtlas imagePath="AllTexture.png">
 <SubTexture name="apple" x="1284" y="221" width="299" height="35" />
 <SubTexture name="arrow" x="1584" y="221" width="20" height="11" />
 <SubTexture name="arrow_left" x="1140" y="975" width="23" height="29" />
 <SubTexture name="arrow_right" x="1939" y="197" width="23" height="29" />
</TextureAtlas>
這樣就可以直接把產出檔案 XML & PNG 餵給 Starling Framework 的 TextureAtlas 用囉~
如果沒錢買 TexturePacker 可以考慮試試看這個免費的軟體~