エモンのブログ(スマホアプリ作成日記) -7ページ目

エモンのブログ(スマホアプリ作成日記)

エモンのブログです。

GooglePlayとAppStoreにアプリをリリースしてます。
「詰将棋パラダイス」4500問無料で公開。
「みんなのしょうぎ」投票型の将棋対局。いずれもソーシャルアプリなので、ソーシャルゲーム作成に興味があるかたは是非ご覧ください。



解説編4をようやくアップしました。
作りかけのまま忙しくなってしまって三ヶ月くらい空いてしまった。

全てactionscriptで動かしているとはいえ、結構内容的な意味で時間かかってしまいますね。
次回は東方キャラを使わせてもらって解説してみようかと思います。
【前編】

■■ ANEで値の取得と広告の表示を行う。その5「AIRに.aneを組み込む」

(5-1)Flashでアプリを作成する。

いつも通りにFlashでアプリを作成しよう。Tugepara.flaという名前でやってます。

(5-2-1)iOS用にパブリッシュ

ーーーーーーーーーーextension.xmlを作成


<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>katsuraba.ane.TugeparaExtension</id>
<versionNumber>1.0.0</versionNumber>
<name>
<text xml:lang="en_US">Hello World</text>
<text xml:lang="ja_JP">こんにちは 世界</text>
</name>
<platforms>

<platform name="iPhone-ARM">
<applicationDeployment>
<nativeLibrary>libTugeAne.a</nativeLibrary>
<initializer>ExtInitializer</initializer>
<finalizer>ExtFinalizer</finalizer>
</applicationDeployment>
</platform>

</platforms>
</extension>


ーーーーーーーーーー
注意点
・xmlnsをextension3.5としている。これは以下のXMLファイルにて合わせる必要がある。
・idセクションではswcを作ったときのASファイルと会わせる
・これはiOS用なので1のみの記述とする。

(5-2-2)platformiOSARM.xmlを作成

どうやらANEの場合、.aでビルドエラーが出てなくても外部フレームワークはリンクが切れている模様。
なので別途platformiOSARM.xmlを作る必要があるようだ。
ーーーーーーーーーーplatformiOSARM.xml


<platform xmlns="http://ns.adobe.com/air/extension/3.5">
<description>An optional description.</description>
<copyright>2011 (optional)</copyright>
<sdkVersion>5.0.0</sdkVersion>
<linkerOptions>
<option>-ios_version_min 5.0</option>
<option>-framework Accelerate</option>
<option>-liconv</option>
<option>-ObjC</option>
</linkerOptions>
<packagedDependencies>
<packagedDependency>ASIHTTPRequest.framework</packagedDependency>
<packagedDependency>XCTest.framework</packagedDependency>
<packagedDependency>AdSupport.framework</packagedDependency>
<packagedDependency>CFNetwork.framework</packagedDependency>
<packagedDependency>JSONKit.framework</packagedDependency>
<packagedDependency>Foundation.framework</packagedDependency>
<packagedDependency>MobileCoreServices.framework</packagedDependency>
<packagedDependency>imobileAds.framework</packagedDependency>
<packagedDependency>SystemConfiguration.framework</packagedDependency>
</packagedDependencies>
</platform>


ーーーーーーーーーー
注意点
・xmlnsをさきほどのextension.xmlと合わせる
・linkerOptions に-ObjCというoptionを追加。※必要かわからない
・packagedDependencies にimobileで指定したframeワークを全て入れる
※なお、これはダイナミックリンクとかでは駄目らしく実態ファイルをTugeAneディレクトリに置く必要がある。
Xcodeの.frameworkで右クリックしてShow in Finderを選択して、実際に置く必要のあるframeworkをコピーしておく。
ただしdylibはいらなかった。

(5-2-3).p12ファイルを作成する

別途Flashを立ち上げて.p12ファイルだけを作っておき、TugeAneディレクトリに保管する
flash_for_ane_ios.p12としました。

(5-2-4).aneを書き出す

FlashのAIRのなかにあるadtコマンドを使って書き出す。
自分の場合は以下にありました
/Applications/Adobe Flash CC/AIR3.6/bin/adt

書き出されるANEファイルをTugeparaExtension.aneとした場合
.swc、library.swf、extension.xml, platform, frameworkをオプションに入れます

以下は一行にて行います。
TugeAneディレクトリに移動します。
含めるframeworkを全部書きます。この時点で.aneの容量が増えますが、最終的に書き出される.ipaの容量は下がっているので大丈夫そう。
ーー
"/Applications/Adobe Flash CC/AIR3.6/bin/adt" -package -storetype pkcs12 -keystore flash_for_ane_ios.p12 -target ane TugeparaExtension.ane extension.xml -swc TugeparaANE_ASLIB.swc -platform iPhone-ARM library.swf libTugeAne.a -platformoptions platformiOSARM.xml ASIHTTPRequest.framework XCTest.framework AdSupport.framework CFNetwork.framework JSONKit.framework Foundation.framework MobileCoreServices.framework imobileAds.framework SystemConfiguration.framework
(5-2-3)で作成した.p12のパスワードを入れると.aneができます。


(5-2-5).aneを実際に使う

Flashを立ち上げます。
ファイル > ActionScript設定 > ライブラリパス
+を押してscriptボタンを押して、さきほど作成した.aneを指定する。

現物のアプリでのActionScriptにてANEのファンクションを呼び出す
import com.katsuraba.TugeparaExtension;
--
var _ane:TugeparaExtension = new TugeparaExtension();
displayTxt.text = _ane.getHelloWorld();//これはHelloWorldを呼び出す。
_ane.getSessionKey();
_ane.setSessionKey();
_ane.displayImobile();
これはムービープレビューでは実行できないので、aneを追加するのは一番最後になろうかと思います。
それまではaneを使うasクラスを別途作っておいて、そのasクラス上でコメントアウトしておけばaneを外したムービープレビューができる、という感じにしておいたほうが良さそうですね。

(5-2-6)iOSまでのSDKを記載する

AIR for iOS設定にて
iOS SDKのテキストボックスができているので
私の場合はここでしたので、これを丸ごと記載。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk

(5-2-7)実機でテスト

ここまででiOSでテストできるました。実機には電波を通すようにしておきましょう。
と、ここで課題が発生。ステータスバーが消えない。フルスクリーンにしたり、objective-cのほうで消すようにしてみたのですが、失敗しました。
あまり優先順位高くないので放っておきました。

2014/01/08追記
なぜかiOS5.1で落ちてしまうので最低OSバージョンをiOS6にしておく。
アプリ-app.xmlにて

<iPhone>
<InfoAdditions><![CDATA[

・・・

<key>MinimumOSVersion</key>
<string>6.0</string>

・・・

]]></InfoAdditions>
<requestedDisplayResolution>high</requestedDisplayResolution>
</iPhone>


(※2014/01/11追記)ちなみにこの記述をするときはFlashCCでのパブリッシュ設定は閉じてから行いましょう。でないと、次から次へと勝手にアプリケーション識別子が自動で書き変わるようです。

(5-3-1)Android用に.aneを作成する

extension.xmlを作る
ーーーーーーーーーextension.xml


<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>katsuraba.ane.TugeparaExtension</id>
<versionNumber>1.0.0</versionNumber>
<name>
<text xml:lang="en_US">Hello World</text>
<text xml:lang="ja_JP">こんにちは 世界</text>
</name>
<platforms>

<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>TugeAne.jar</nativeLibrary>
<initializer>tugepara.ane.android.TugeparaExtension</initializer>
<finalizer>tugepara.ane.android.TugeparaExtension</finalizer>
</applicationDeployment>
</platform>

</platforms>
</extension>


ーーーーーーーーー
・initializerセクションには.jarファイルを作ったときのパッケージ名を記載する

(5-3-2)実際に.aneを使う

import com.katsuraba.TugeparaExtension;
--
var _ane:TugeparaExtension = new TugeparaExtension();
_ane.displayImobile();
こんな感じ

(5-3-3)コマンドをたたく

"/Applications/Adobe Flash CC/AIR3.6/bin/adt" -package -storetype pkcs12 -keystore flash_for_ane_ios.p12 -target ane TugeparaExtension.ane extension.xml -swc TugeparaANE_ASLIB.swc -platform Android-ARM library.swf TugeAne.jar
生成された.aneをFlashファイルと同階層にコピーする

(5-3-4)Tugeane-app.xmlのandroidセクション

Android用にネイティブ拡張する際にFlashCCからだとアプリケーション識別子であるTugepara-app.xmlが書き換えられてしまうためコマンドラインでパッケージすることにした。
なお、FlashCCのパブリッシュ設定の画面を見ているだけで上書きされます。スタンド攻撃かよ。
androidセクション内で記載されたところは上書きされないので、ここにimobileのPUBLISHER_IDを記載しておく。
ーーーーーーーーー


<android>
<manifestAdditions><![CDATA[<manifest>
<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<meta-data android:name="i-mobile_Testing" android:value="false" />
<meta-data android:name="i-mobile_Publisher_ID" android:value="****" />
</application>
</manifest>]]></manifestAdditions>
</android>


ーーーーーーーーー
・そもそものアプリ名-app.xmlの仕組みとしてandroidセクション内に記載されたものはパッケージ化する際に上書きされない。
Androidアプリを作る際に作るmanifest.xmlをここから操作できる。タグセクションに追加記載が可能になる。
・permissionにandroid.permission.INTERNETとandroid.permission.ACCESS_NETWORK_STATEを追加
・applicationセクション内のmeta-dataにimobileのテストモードとpublisherIdを記載

(5-3-4)apkパッケージ生成

コマンドもしくはFlashCCからパブリッシュ 
自分はランタイムを埋め込まないと動かなかったのでFlashCCかららのパブリッシュしました。
なおコマンドではこんな感じになる。

Flashファイルの置いてあるディレクトリに.p12ファイル、aneファイルを引数に含める
"/Applications/Adobe Flash CC/AIR3.6/bin/adt" -package -target apk -storetype pkcs12 -keystore tugepara_android.p12 Tugepara.apk Tugepara-app.xml tugepara.swf AppIconsForPublish/icon_shogi_tuge_48.png AppIconsForPublish/icon_shogi_tuge_36.png AppIconsForPublish/icon_shogi_tuge_72.png AppIconsForPublish/icon_shogi_tuge_144.png -extdir .

ついでに出てきたapkをzipに変更して中身のmanifest.xmlを確認するには
"/AIRSDK_Compiler 3.9/lib/android/bin/aapt" l -a Tugepara.apk
とする。この中でpublisherIDが入っているか確認できる。

生成された.apkをどこかサーバに置いてandroid端末でダウンロードすることで確認する。

(6)感想

プログラマ人生の中で5本の指に入るほど難しく、長くハマったと思う。
1ミリもミスできないので本当にスペルミスは注意ですね。以下参考サイトでのスペルミスなのでご注意ください。
(正)getHelloWorld が (誤)gatHelloWorld
(正)extension が (誤)extention
(正)iconTitleEnable が (誤)iconTiileEnable ※imobileの資料
この記事も余分があったり、間違ってることを平気で書いてるかもしれません。この記事もいずれ修正していくことになりましょう。
また汎用性がなく、.aneを公開できるようなものになっていません。あくまで「.aneを作る」ところに目線を置いてます。
いずれ.aneを公開したいと思います。※imobileさんからの連絡をお待ちしてます。
次はアプリ内課金に挑戦したいところ。そのまえにまだまだネイティブの知識をつけていかないといけませんね。
そもそもAIRは可能性があるのかという疑問もありましたが、Android4.4やスマートTVでのAIRの動作を見るとかなり奇麗に表示されていました。
解像度が今後上がっていくことを考えれば、ベクターデータの取り扱いが神がかっているFlashをツールとして選択するのは非常に有力ですね。

(7)参考

http://mushikago.com/i/?p=996
http://tnker.com/?p=2612
http://kazutoyo.jp/archives/17
■■ ANEで値の取得と広告の表示を行う。その1「ANEとは?」

(1-1)AIR for Android, AIR for iOSについて

Adobe AIRでスマホ向けのアプリを作れます。Flashの知識があれば確実にこちらでアプリを作っていきたいですね。
ところがAIRだけだと不都合が生じる場合があります。ローカルへの値の保存はSharedObjectというものがあります。このSharedObjectはAndroidなら大丈夫ですが、iOSではアプリをアップデートしたタイミングで情報が失われてしまいます。
データの永続化については他に失われない方法があるのかもしれませんが、わかりませんでした。
データを端末に保存するという観点から見るにネイティブの機能を使って保存するのが自然に見えます。
なのでAIRからネイティブの機能を使っていきたいところです。
そしてAdobeAIRではネイティブ拡張というものがあり、それを使うとネイティブの機能を使うことができます。
これをAIR Native Extension(以下ANE)と言います。

ANEでは、ネイティブ・・・つまりObjective-cやJavaと連携して、
1.値を取得する。
2.処理を委譲する。
ことができます。
永続化した値の受け渡しは1番、広告の表示は2番ということになります。

(1-2)用意&作成するファイル

・現実で動かすアプリ (app.swf app.fla app.xml)
・スターティックライブラリ (.a .jar)
・ネイティブ拡張ファイル (.swc library.swf)
・ANE (.ane)

現物のアプリに組み込む流れとしては、まずネイティブで記述されたスターティックライブラリを作成し、
それを.aneとしてパッケージ。.aneを現物のアプリに組み込む。という感じです。

(1-3)環境

私が実際にANEを組み込んだ環境
Xcode5.0.1
Android Developer Tools Build: v22.3.0-887826 ※Eclipse
Adobe Flash CC
ADネットワークはimobileを選択。広告枠の承認まで通しておく。
Flash Builder 4.7

そして今回アプリとして作るものは「黄楊の輝きの詰将棋」でしたのでTugeAne という名前がベースになっていきます。
またunDisplayImobileというメソッド用意してますけど、自分の場合広告を含めて画面デザインしているので、消すテストはしてません。

■■ ANEで値の取得と広告の表示を行う。その2「ネイティブ拡張ファイルを作成する」

(2-1)swcを作ります

Flash Builder > ファイル > 新規 > Flexライブラリプロジェクト
「AIRライブラリを含める」にチェックを入れる
プロジェクト名:TugeparaANE_ASLIBとします。

(2-2)TugeparaExtensionのアクションスクリプトクラスを作成

srcの下にflash.events.EventDispatcherを継承してActionScriptクラスを作成
パッケージ名:com.katsuraba
名前:TugeparaExtension
ーーーーーーーーーーTugeparaExtension.as


package com.katsuraba
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.external.ExtensionContext;

public class TugeparaExtension extends EventDispatcher
{
private var context:ExtensionContext;
public function TugeparaExtension(target:IEventDispatcher=null)
{
super(target);
context= ExtensionContext.createExtensionContext("katsuraba.ane.TugeparaExtension", "type");
}

public function getHelloWorld():String
{
return context.call("GetHelloWorld") as String;
}

public function getSessionKey():String
{
return context.call("GetSessionKey") as String;
}

public function setSessionKey(sessionKey:String):void
{
context.call("SetSessionKey", sessionKey);
}

public function displayImobile():void
{
context.call("DisplayImobile");
}

public function unDisplayImobile():void
{
context.call("UnDisplayImobile");
}

public function dispose():void
{
return context.dispose();
}

}
}

ーーーーーーーーーー


注意点
context= ExtensionContext.createExtensionContext("katsuraba.ane.TugeparaExtension", "type");

katsuraba.ane.TugeparaExtension
はご記憶ください。
ここではセッションキーの出し入れと、広告の表示・非表示用の関数を用意しました。あと動作確認のためのHelloWorldも。

(2-3)ビルド

プロジェクトのプロパティ > ActionScriptライブラリコンパイラー > 追加引数コンパイラー
以下を追加
-swf-version 13

ビルドするとプロジェクトのbinディレクトリにTugeparaANE_ASLIB.swcが生成される。
これを別のディレクトリに保管しておく。
TugeparaANE_ASLIB.swcの名前をTugeparaANE_ASLIB.zipに変更して、それを解凍。
なかにlibrary.swfがあるから保管しておく。

今後この保管用ディレクトリをTugeAneディレクトリと呼ぶことにします。

■■ ANEで値の取得と広告の表示を行う。その3「iOS用のスターティックライブラリを作成する」

(3-1)Xcodeで広告データ表示や値の受け渡し処理がある.aを作成する

Xcodeを立ち上げて
File > NEW > Project を選択
iOS > cocoa touch Static Libraryを選択
ProjectName = 任意。TugeAne
project作成

(3-2)必要ライブラリの追加作業

imobileの資料に従って以下を追加。これは後々aneをパッケージにするときにも出てくるので重要です。
・CFNetwork.framework
・libz.dylib
・MobileCoreServices.framework
・SystemConfiguraion.framework
・AdSupport.framework

Xcode > プロジェクト名 > TARGETS > Build Phases > Link Binary With Libraries
「+」ボタンを押して上記のframeworkを追加していく。
AdSupport.frameworkのみoptionalにする
またimobileから提供されている
・ASIHTTPRequest.framework
・JSONKit.framework
・imobileAds.framework
を外部frameworkとして追加する。ドラッグ&ドロップでプロジェクトにコピーし、
「destination」にチェックを入れてコピーする。
これも
Link Binary With Librariesに追加する。

AIRSDK_Compiler/includeのFlashRuntimeExtensions.hを
プロジェクトディレクトリに「destination」にチェックを入れてコピー。

(3-3)コードの記述

ーーーーーーーーーーTugeAne.h


#import
#import "FlashRuntimeExtensions.h"

@interface TugeAne : NSObject

@end

FREObject GetHelloWorld(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);

FREObject GetSessionKey(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);

FREObject SetSessionKey(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);

FREObject DisplayImobile(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);

FREObject UnDisplayImobile(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);


void ContextInitializer(
void* extData,
const uint8_t* ctxType,
FREContext ctx,
uint32_t* numFunctionsToTest,
const FRENamedFunction** functionsToSet
);

void ContextFinalizer(FREContext ctx);

void ExtInitializer(
void** extDataToSet,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet
);
void ExtFinalizer(void* extData);


ーーーーーーーーーー

ーーーーーーーーーーTugeAne.m


#import "TugeAne.h"
#import "TugeAneViewController.h"
#import "imobileAds/IMobileAdIconView.h"

@implementation TugeAne

@end

TugeAneViewController *tugeAneAdView;

FREObject GetHelloWorld(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
const char *str = "HelloWorld !";
FREObject retStr;
FRENewObjectFromUTF8(strlen(str)+1, (const uint8_t *)str, &retStr);
return retStr;
}

FREObject GetSessionKey(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
//NSUserDefaultsからセッションキーを取得
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = @"tugeane_session_key";
NSString *skey = [ud stringForKey:key];
if(skey==nil) skey = @"0";
const char *str = [skey UTF8String];

FREObject retStr;
FRENewObjectFromUTF8(strlen(str)+1, (const uint8_t *)str, &retStr);

return retStr;
}

FREObject SetSessionKey(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
//NSUserDefaultsへセッションキーを保存
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = @"tugeane_session_key";
NSString *sessionKey = @"";

uint32_t length;
const uint8_t *value;
FREGetObjectAsUTF8(argv[0], &length, &value);

[ud setObject:[NSString stringWithUTF8String: (char*) value] forKey:key];
[ud synchronize];
return NULL;
}

FREObject DisplayImobile(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
//AD用のviewを追加する
tugeAneAdView = [[TugeAneViewController alloc] init];
//広告の位置を決定する。サイズによって変わる。320x568の場合はサイズを大きくする
UIScreen *sc = [UIScreen mainScreen];
CGRect rect0 = sc.bounds;
int startY = rect0.size.height-50;
int size = 50;
//320x568なら広告の位置を変えます。
if(rect0.size.height == 568 || rect0.size.height == 1136) {
startY = rect0.size.height-90;
size = 70;
}
//iPadなら
if(rect0.size.height == 568 || rect0.size.height == 1136) {
//
}
CGRect rect = CGRectMake(0, startY, rect0.size.width, size);
tugeAneAdView.view.frame = rect;
tugeAneAdView.adSize = size;
tugeAneAdView.adWidth = rect0.size.width;
[tugeAneAdView showAd];
//windowに追加
id delegate = [[UIApplication sharedApplication] delegate];
UIWindow * win = [delegate window];
[win addSubview:tugeAneAdView.view];
return NULL;
}

FREObject UnDisplayImobile(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
tugeAneAdView = nil;
return NULL;
}

void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet) {
*numFunctionsToTest = 5;
FRENamedFunction* func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*5);
func[0].name = (const uint8_t*)"GetHelloWorld";
func[0].functionData = NULL;
func[0].function = &GetHelloWorld;

func[1].name = (const uint8_t*)"GetSessionKey";
func[1].functionData = NULL;
func[1].function = &GetSessionKey;

func[2].name = (const uint8_t*)"SetSessionKey";
func[2].functionData = NULL;
func[2].function = &SetSessionKey;

func[3].name = (const uint8_t*)"DisplayImobile";
func[3].functionData = NULL;
func[3].function = &DisplayImobile;

func[4].name = (const uint8_t*)"UnDisplayImobile";
func[4].functionData = NULL;
func[4].function = &UnDisplayImobile;

*functionsToSet = func;
}
void ContextFinalizer(FREContext ctx) {
return;
}

void ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet) {
*extDataToSet = NULL;
*ctxInitializerToSet = &ContextInitializer;
*ctxFinalizerToSet = &ContextFinalizer;
}
void ExtFinalizer(void* extData) {
return;
}


ーーーーーーーーーー
注意点
・ファンクションが増えているため
*numFunctionsToTest = 5;
FRENamedFunction* func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*5);
としている。
・ウィンドウサイズを大きくしてしまうと、肝心のAIRアプリのタッチが利かなくなる
途中の
CGRect rect = CGRectMake(0, startY, rect0.size.width, size);
tugeAneAdView.view.frame = rect;
の部分はあくまで広告を表示するべき場所を指定する。ただし3.5インチや4インチ、iPadなどで分岐する必要がありそう。
・NSUserDefaultsでセッションキーを取得するときに最初nilになっているので、その場合に対応(2014/01/06追記)

広告表示のためのviewを作成する。
Xcode > プロジェクト名 > NEW File > cocoa touch > Object-C Class
にてUIViewControllerを継承して新規クラスを作成する

ヘッダファイルにはimobileからもらった各種IDを追加していく。
プラスでUIkit.frameworkもビルドパスに追加する。
ーーーーーーーーーーTugeAneViewController.h


#import

@interface TugeAneViewController : UIViewController

@property int adSize;
@property int adWidth;

#define YOUR_PUBLISHER_ID (int)*****
#define YOUR_MEDIA_ID (int)*****
#define YOUR_SPOT_ID (int)*****

-(void) showAd;

@end


ーーーーーーーーー
ここでimobileからもらった各種IDを入れよう。

ーーーーーーーーーTugeAneAdView.m


#import "TugeAneViewController.h"
#import "FlashRuntimeExtensions.h"
#import "imobileAds/ImobileAdIconView.h"
#import "imobileAds/IMobileAdIconViewParams.h"

@interface TugeAneViewController () {

}

@end

@implementation TugeAneViewController

@synthesize adSize;
@synthesize adWidth;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
}

-(void) showAd {
IMobileAdIconViewParams *params = [[IMobileAdIconViewParams alloc] init];
params.iconSize = adSize;
params.iconTitleEnable = NO;
CGRect frame = CGRectMake(0, 0, adWidth, adSize);
IMobileAdIconView *imAdIconView = [[IMobileAdIconView alloc] initWithFrame:frame
publisherId:YOUR_PUBLISHER_ID mediaId:YOUR_MEDIA_ID
spotId:YOUR_SPOT_ID
iconNumber:4
params:params
testMode:NO];
[self.view addSubview:imAdIconView];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


ーーーーーーーーーー

(3-4).aの書き出し

プロジェクト > TARGETS > BuildSettings > iOS Deployment Target
iOSのバージョンを指定する。デバッグ端末がiOS5.1だったので、とりあえずiOS5.0くらいにしておいた。(2014/01/07追記)
※ただしiOS7で書き出したときもiOS6で動作していた。うーむ?わからん
また、何回も実行することを考えて、ゴミが残らないようにCleanしてからBuildしましょう。
Xcode > Product > Clean
Xcode > Product > Build
Xcode > Product > Build For Profile

プロジェクトエクスプローラー上の
プロジェクト名 > Productsに
libTugeAne.a ができているので右クリックしてShow in Finderして.aファイルをTugeAneディレクトリに確保しておく

注意点(2014/01/10追記)
AIR for iOSで書き出す場合に「無効な入力値です」と言われることがある。この場合、ここでのBuild時にシュミレーター用のBuildにしている可能性がある。対応策は「Macから一旦実機を外して」Buildする。何を言ってるのか自分でもわからないが、そういうことらしい。

■■■ ANEで値の取得と広告の表示を行う。その4「Android用のライブラリを作成する」

(4-1)Eclipseで広告データ表示や値の受け渡し処理がある.jarを作成する

ADTを立ち上げて
パッケージエクスプローラ > 右クリック > New > Project > Android Application Project
ApplicationName = TugeAne
パッケージ名:com.example.tugeane
Nextボタンを押下(重要)
create custom laucher icon と Create Activityのチェックを外す。

(4-2)必要ライブラリの追加&ビルドパス

Project > Propaties > Libraries > Add External Jars
ここからFlashRuntime.jarとimobileからもらったi-mobileSDK.jarをビルドパスに含める
FlashRuntime.jarは
AIRSDK_Compiler 3.9/lib/android ここにある。

(4-3)パッケージを作成してコードの記述

パッケージをsrc上に2個新規作成する。
tugepara.ane.androidとtugepara.ane.android.function
tugepara.ane.androidにjavaファイル作成
ーーーーーーーーーーTugeparaContext.java


package tugepara.ane.android;

import java.util.HashMap;
import java.util.Map;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;

import tugepara.ane.android.function.DisplayImobileFunction;
import tugepara.ane.android.function.GetHelloWorldFunction;
import tugepara.ane.android.function.GetSessionKeyFunction;
import tugepara.ane.android.function.SetSessionKeyFunction;
import tugepara.ane.android.function.UnDisplayImobileFunction;

public class TugeparaContext extends FREContext {
public TugeparaContext() {
}

public Map getFunctions() {
HashMap result = new HashMap();

//関数の定義
result.put("GetHelloWorld", new GetHelloWorldFunction());
result.put("GetSessionKey", new GetSessionKeyFunction());
result.put("SetSessionKey", new SetSessionKeyFunction());
result.put("DisplayImobile", new DisplayImobileFunction());
result.put("UnDisplayImobile", new UnDisplayImobileFunction());

return result;
}

public void dispose() {
//コンテキストの破棄処理
}
}


ーーーーーーーーーーTugeparaExtension.java


package tugepara.ane.android;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREExtension;

import tugepara.ane.android.TugeparaContext;

public class TugeparaExtension implements FREExtension {
public TugeparaExtension() {
}
public FREContext createContext(String arg) {
//コンテキスト作成
FREContext context = new TugeparaContext();
return context;
}
public void initialize() {
//ネイティブ拡張初期化処理
}
public void dispose() {
//ネイティブ拡張破棄処理
}
}



ーーーーーーーーーー

tugepara.ane.android.functionにFunction系javaファイル作成
ーーーーーーーーーーGetHelloWorldFunction.java


package tugepara.ane.android.function;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;
import com.adobe.fre.FREWrongThreadException;

public class GetHelloWorldFunction implements FREFunction {

public GetHelloWorldFunction() {
}

public FREObject call(FREContext arg0, FREObject arg1[]) {
try {
return FREObject.newObject("Hello tugepara 1");
} catch (FREWrongThreadException e) {
e.printStackTrace();
return null;
}
}
}



ーーーーーーーーーー

ーーーーーーーーーーDisplayImobileFunction.java


package tugepara.ane.android.function;

import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;

public class DisplayImobileFunction implements FREFunction {

private jp.co.imobile.android.AdIconView ad;
public DisplayImobileFunction() {
}

public FREObject call(FREContext arg0, FREObject arg1[]) {
try {
int mediaId = ******;
int spotId = ******;
//アクティビティ
Activity activity = new Activity();
activity = arg0.getActivity();
DisplayMetrics metrics = new DisplayMetrics();
((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);
Context con = (Context)activity;
this.ad = jp.co.imobile.android.AdIconView.create(con, mediaId, spotId, 4);
//位置設定
int wc = LinearLayout.LayoutParams.WRAP_CONTENT;
int height = (int)(75.0f * metrics.density + 0.5f);
FrameLayout.LayoutParams adParams = new FrameLayout.LayoutParams(wc, height);
adParams.gravity = Gravity.BOTTOM;
//表示
activity.addContentView(this.ad, adParams);
this.ad.start();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}


ーーーーーーーーーー

ーーーーーーーーーーUnDisplayImobileFunction.java

※省略します。今後の課題です。

ーーーーーーーーーー

ーーーーーーーーーーGetSessionKeyFunction.java


package tugepara.ane.android.function;

import android.app.Activity;
import android.content.SharedPreferences;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;
import com.adobe.fre.FREWrongThreadException;

public class GetSessionKeyFunction implements FREFunction {

public GetSessionKeyFunction() {
}

public FREObject call(FREContext arg0, FREObject arg1[]) {
try {
//アクティビティ
Activity activity = new Activity();
activity = arg0.getActivity();
//preferenceから取得
SharedPreferences sp = activity.getSharedPreferences("tugepara_sessionkey", Activity.MODE_PRIVATE);
String sessionKey = sp.getString("tugepara_sessionKey", "0");
return FREObject.newObject(sessionKey);
} catch (FREWrongThreadException e) {
e.printStackTrace();
return null;
}
}
}


ーーーーーーーーーー

ーーーーーーーーーーSetSessionKeyFunction.java


package tugepara.ane.android.function;

import android.app.Activity;
import android.content.SharedPreferences;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREInvalidObjectException;
import com.adobe.fre.FREObject;
import com.adobe.fre.FRETypeMismatchException;
import com.adobe.fre.FREWrongThreadException;

public class SetSessionKeyFunction implements FREFunction {

public SetSessionKeyFunction() {
}

public FREObject call(FREContext arg0, FREObject arg1[]) {
try {
//アクティビティ
Activity activity = new Activity();
activity = arg0.getActivity();
//データ保存
FREObject param = arg1[0];
String sessionKey = param.getAsString();
SharedPreferences sp = activity.getSharedPreferences("tugepara_sessionkey", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("tugepara_sessionkey", sessionKey);
editor.commit();
} catch (FREWrongThreadException e) {
e.printStackTrace();
} catch (FREInvalidObjectException e) {
e.printStackTrace();
} catch (FRETypeMismatchException e) {
e.printStackTrace();
}
return null;
}
}


ーーーーーーーーーー

(4-4)Fat Jarの取得

Eclipse > Help > Install New SoftWareにて
WorkWithのところ右側の「Add」ボタンを押す。

Name : fat jar
Location : http://kurucz-grafika.de/fatjar
インストール後Eclipseを再起動。

(4-5).jarの作成

Eclipse > Project > Clean
プロジェクトを右クリック > Build Fat Jar
にてjarの名前を決める。Tugeane.jarとしてNextボタンをクリック。
imobile-SDK.jarとプロジェクトのパッケージのみをチェックする。
他のjar、たとえばFlashRuntimeExtensions.jar, anrdoid.jarにもチェックを入れてしまうと、
java.lang.IllegalArgumentException: already added: Lcom/adobe/fre/FREArray;
がapkを作成するときに出てくる。とくにFlash Runtime .jarは実はfat jarに含める必要はなく、
考えてみればFatJarがなくてもHelloANEくらいならできていたことを思い出したのであった。

生成されたTugeane.jarをTugeaneAndroidディレクトリに保管する。
※Android用とiOS用とで分けてます。
結局のところiOSとAndroidで画面比率が違うから同じFlashにはならないし、同じane使うことはないので。

【後編】へ