2015年6月11日 星期四

[Corona SDK] How to add advertisement in my APP - using AdMob V2

We may want to integrate advertising bar or page in our APP.
Let's take AdMob as our example.

First, you need to visit AdMob to apply for an account.
After that, you can add the APP which you want to has Ads.
I will skip the detailed steps for that.
Basically, there have two types of Ads for AdMob.
One is called banner, and the other one is interstitial.
If your APP will be for Android and iOS platforms both, you need to apply for the Ads Unit ID separately.
That is, you will get 4 Ads Unit IDs.
Let's check the coding part.

Add plugin
First, we need to add plugin in build.settings:
settings =
{
 plugins =
    {
        ["plugin.google.play.services"] =
        {
            publisherId = "com.coronalabs"
        },        
    },
 android =
    {

        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
        },
    },
}
As you can see, we also need to add access right for Android system.

Show the Ads
To show the Ads, we just need to program as follow:
local ads = require( "ads" )
local bannerAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your iOS banner
local interstitialAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your iOS interstitial
if ( system.getInfo("platformName") == "Android" ) then
    bannerAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your Android banner
    interstitialAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your Android interstitial
end

local adProvider = "admob"

local function adListener( event )
    local msg = event.response
    print( "Message from the ads library: ", msg )
 if (event.type == "banner") then
     if ( event.isError ) then
         print( "Error, no bannser ad received", msg )
     else
         print( "Got one banner AD" )
     end
 elseif(event.type == "interstitial") then
        if ( event.isError ) then
            print( "Error, no interstitial ad received", msg )
        elseif ( event.phase == "loaded" ) then
            print("interstitial ads got...")
        elseif ( event.phase == "shown" ) then
            print("interstitial has been shown and closed") 
        end
 end
end

ads.init( adProvider, bannerAppID, adListener )

ads.show( "banner", { x=0, y=0, appId=bannerAppID} )--show banner ads

ads.show( "interstitial", { x=0, y=0, appId=interstitialAppID} )--show banner ads

ads.hide() -- hide the ads
The bannerAppID and interstitialAppID are the Unit ID that  you get from AdMob websit.

We only need to call ads.init() one time.
You can pass in any type of appId.
In ads.show(), we pass in the Ads type and related appId then.

Banner Ads
ads.show( "banner", { x=0, y=0, appId=bannerAppID} ) can show the banner Ads.
The parameters {x=0, y=0} mean than we what to locate the banner in the top side.
You can decide whether to show it, as long as it will not affect the process of game.
If you want to have Ads, you'd better consider the best location for it when you are doing UI layout.

In general, we will put it in the top side or bottom side.
The reserved width should be screen-wide.
However, what about the height?
There is so called Smart Banners for AdMob.
When it is showing in phones, its height will be 32 for Landscape mode, and will be 50 for Portrait mode.
When it is showing in tablets, its height will be 90.
However, I did get banner with height in one of my phones.
Just in case, we can reserve 90 or even 100 height if possible.
It will be suitable for all cased.

Interstitial Ads 
For Interstitial Ads, it will occupy the whole screen.
It doesn't matter for what values of parameters x and y you pass in.
You should not show Interstitial Ads too frequent.
Otherwise, the user will feel that it is annoying.

The loading of Interstitial Ads may take a while.
We'd better load it in advance before showing it, as shown below:
ads.load( "interstitial", { appId=interstitialAppID } ) --load the "interstitial" ads in advance
Once it has been loaded, it will show up immediately when we call ads.show()
Before showing, we need to check if it has been loaded.
if(ads.isLoaded("interstitial"))then
 ads.show( "interstitial", { x=0, y=0, appId=interstitialAppID } )
end
Please note that once we call ads.show(), even we call ads.show( "banner", { x=0, y=0, appId=bannerAppID} ) to show the banner ads, ads.isLoaded("interstitial") will be false.
That is, the pre-loaded data will be cleared.
We need to call ads.load( "interstitial", { appId=interstitialAppID } ), ads.isLoaded("interstitial") again after we calling ads.show().
However, for Android system, once we have ever called ads.load( "interstitial", { appId=interstitialAppID } ), it will reload the data automatically.
That is, ads.isLoaded("interstitial") will be true later automatically after it has got the Interstitial Ads.
For iOS system, it will not reload it automatically. Not sure the reason.

The Ads function can only work on real devices.
We'd better add some judgement for above codes.
That is, adding system.getInfo( "environment" ) ~= "simulator" in our codes.

[Corona SDK] 如何加入廣告 - AdMob V2

很多人開發的APP都是免費的,
為了獲利,難免都會加作廣告,
我們就以很多人常用的AdMob來當範例。

首先,您必須到AdMob去申請一個帳號,
然後新增你要加入廣告的APP,
細節這邊就先不提了,
原則上,AdMob有二個樣式,
一個是橫幅(banner),一個是插頁(interstitial),
如果你是同時要支援Android和iOS平台,
那各別都要申請,
也就是說,你總共會需要4個廣告編號。

接著我們來看程式部份:

Add plugin
首先,我們需要在build.settings加入plug-in:
settings =
{
 plugins =
    {
        ["plugin.google.play.services"] =
        {
            publisherId = "com.coronalabs"
        },        
    },
 android =
    {

        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
        },
    },
}
另外,Android需要增加網路存取的權限

Show the Ads
要顯示廣告,只要按照以下寫法即可
local ads = require( "ads" )
local bannerAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your iOS banner
local interstitialAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your iOS interstitial
if ( system.getInfo("platformName") == "Android" ) then
    bannerAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your Android banner
    interstitialAppID = "ca-app-pub-3001435268155896/xxxxxxxxxx"  --for your Android interstitial
end

local adProvider = "admob"

local function adListener( event )
    local msg = event.response
    print( "Message from the ads library: ", msg )
 if (event.type == "banner") then
     if ( event.isError ) then
         print( "Error, no bannser ad received", msg )
     else
         print( "Got one banner AD" )
     end
 elseif(event.type == "interstitial") then
        if ( event.isError ) then
            print( "Error, no interstitial ad received", msg )
        elseif ( event.phase == "loaded" ) then
            print("interstitial ads got...")
        elseif ( event.phase == "shown" ) then
            print("interstitial has been shown and closed") 
        end
 end
end

ads.init( adProvider, bannerAppID, adListener )

ads.show( "banner", { x=0, y=0, appId=bannerAppID} )--show banner ads

ads.show( "interstitial", { x=0, y=0, appId=interstitialAppID} )--show banner ads

ads.hide() -- hide the ads
上面的bannerAppID和interstitialAppID,就是你去AdMob申請得到的廣告unit id,

ads.init()只要做一次即可,
之後在ads.show()裡再帶入想要顯示的ads型態和對應的appId即可,

橫幅廣告Banner Ads
ads.show( "banner", { x=0, y=0, appId=bannerAppID} )可以顯示橫幅的廣告,
上面的參數裡,x=0, y=0表示要顯示在最上方,位置你可以自己決定,
所以程式的UI配置要先決定, 以不影響遊戲進行就好,
大部份不是最上方就最下方,
然後預留給它的寬度是整個螢幕寛度,
那高度呢?要預留多高才行?
AdMob有所謂的Smart Banners,
手機上面, 在landscape模式時,高度是32,
在portrait模式時,高度是50,
平板上面,高度就是90,
不過,我有遇過手機上得到的banner高度是70左右,
為了保險起見,預留90或者100就能適合各種情形 

插頁廣告Interstitial Ads 
插頁廣告會佔用全螢幕,所以ads.show()裡傳入的x,y值就沒有差別,
插頁廣告最好不要太多,不然使用者可能會覺得很煩,
另外,插頁廣告下載需要一段時間,
所以我們最好是先預載到手機或平板上,如下面的方法:
ads.load( "interstitial", { appId=interstitialAppID } ) --load the "interstitial" ads in advance
這個動作可以程式一啓動就先做,
要顯示廣告時,先判斷是否已下載回來了:
if(ads.isLoaded("interstitial"))then
 ads.show( "interstitial", { x=0, y=0, appId=interstitialAppID } )
end
要注意的是,如果你呼叫過ads.show(),
即使是ads.show( "banner", { x=0, y=0, appId=bannerAppID} ),
ads.isLoaded("interstitial")都會變成false,
在Android平台上,只要曾經呼叫過ads.load( "interstitial", { appId=interstitialAppID } ),
之後呼叫ads.show()後,雖然ads.isLoaded("interstitial")會被清為false,
但是它會自已在reload,
可是在iOS平台,每次都必須呼叫ads.load( "interstitial", { appId=interstitialAppID } ), ads.isLoaded("interstitial")才會再度為true,

Ads只有在實機上才有用,
所以,上面的程式,最好是可以加上判斷,
也就是說system.getInfo( "environment" ) ~= "simulator"時才執行

2015年6月3日 星期三

[Corona SDK] How to localize your APP title - Android part

In [Corona SDK] How to localize your APP title - iOS part, we have learn how to localize the APP title for iOS system.
For Android APP, such function is available in Enterprise version.
However, I think most people are using free version.
So..how should we do?

The Android apk file is just a compressed format.
First, we need to prepare two tools, one is apktool, the other is zipalign.
These two tools should be found in Android SDK.
If you don't have, please search them on internet.

We can put the apk file and related tools in the same folder.

myAndroidApp.apk is the target apk we want to modify.
For convenience, I create some bat files.
Let's look at their contents.
(The following actions are done in Windows environment)

apktool.bat
if "%PATH_BASE%" == "" set PATH_BASE=%PATH%
set PATH=%CD%;%PATH_BASE%;
java -jar -Duser.language=en "%~dp0\apktool.jar" %1 %2 %3 %4 %5 %6 %7 %8 %9
unpack.bat
apktool d %1
pack.bat
apktool b %1 -o %2
sign.bat
jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore "my.keystore" %1 yourPassword
zipalign -v 4 %1 zip-%1 
del %1 
ren zip-%1 %1
Remember to change yourPassword as yours in sign.bat

1. In command shell, input unpack "myAndroidApp.apk"
It will unpack myAndroidApp.apk to the folder myAndroidApp
2. Enter the folder myAndroidApp\res\values, if the file strings.xml is there, open it.
If not, create a new one. 
Modify its content as:
<resources>
    <string name="appTitle">Your Title</string>
</resources>
3. If we want to add other languages, say French.
We need to create a new folder values-fr under myAndroidApp\res.
Again, create strings.xml under folder values-fr.
The content of  strings.xml is the same as what we do in step, except for that Your Title should be translated into French or any other titles you want.
4. For supporting a new language, we just repeat step 3, except for that the folder names are different.
For example, values-ja is for Japanese, while values-it is for Italian.
5. Return to the folder myAndroidApp, open file AndroidManifest.xml.
Search android:label="xxx" and change it to be android:label="@string/appTitle".
There should have two places. Both of them need to be modified.
Someone said that FileContentProvider should be modified as well.
However, if you do it, you will get run time error when you start the  APP.
6. In command shell, input  pack myAndroidApp  newApp.apk
7. In command shell, input  sign newApp.apk

Done...




[Corona SDK] 如何讓您你的APP名稱也支援多國語言 - Android篇

在[Corona SDK] 如何讓您你的APP名稱也支援多國語言 - iOS篇裡我們提到如何將iOS APP的名稱顯示成不同的語言,
Android APP的話,要Enterprise版本的Corona SDK才能支援,
不過,我想大部份人可能都只是使用免費版,
那要如何解決呢?

android的apk檔,基本上只是一個壓縮檔,
所以,我們可以稍微動一下手腳,

首先,你要準備二個工具,
一個是apktool,一個是zipalign,
這二個工具在Android SDK裡會有,如果你手上沒有,就自己上網找一下,

我們將build出來的apk檔和所需要的工具都放在同一個目錄以方便操作,
myAndroidApp.apk是要修改的原始apk檔,
為了方便,所以我又造出了幾個bat檔,
我們先來看看各個bat檔的內容
(我是在Windows作業系統操作的)
apktool.bat
if "%PATH_BASE%" == "" set PATH_BASE=%PATH%
set PATH=%CD%;%PATH_BASE%;
java -jar -Duser.language=en "%~dp0\apktool.jar" %1 %2 %3 %4 %5 %6 %7 %8 %9
unpack.bat
apktool d %1
pack.bat
apktool b %1 -o %2
sign.bat
jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore "my.keystore" %1 yourPassword
zipalign -v 4 %1 zip-%1 
del %1 
ren zip-%1 %1
sign.bat裡的yourPassword記得改成你自己的

1. 在命令視窗裡輸入 unpack "myAndroidApp.apk",
它會將myAndroidApp.apk解壓縮到目錄myAndroidApp,
2. 進入myAndroidApp\res\values,如果裡面已有strings.xml,打開它來編輯,
如果沒有,新增一個,將其內容修改如下:
<resources>
    <string name="appTitle">Your Title</string>
</resources>
3. 如果想要增加其它語言,例如繁體中文,
那就在myAndroidApp\res下面新增一個目錄values-zh-rTW,
在目錄values-zh-rTW裡面同樣新增strings.xml,
內容和上面的一樣,只不過Your Title要改成中文的名稱
4. 要增加幾個語言,步驟3就重複做幾次,只是新增的目錄不同,
例如日文是values-ja,義大利文是values-it等等
5. 回到目錄myAndroidApp下面,打開AndroidManifest.xml,
尋找android:label="xxx",將它改成android:label="@string/appTitle",
應該會有二個地方,全部都要改,
另外,國外網站有人說FileContentProvider也要修改,那是不對的,
如果你修改它,程式執行時會得到錯誤訊息
6. 在命令視窗裡輸入 pack myAndroidApp  newApp.apk
7. 在命令視窗裡輸入 sign newApp.apk,
完成



2015年6月2日 星期二

[Corona SDK] How to localize your APP title - iOS part

In [Corona SDK] How to support multi-language (localize your APP),
we know how to support multi-languages.
However, the method mentioned above only worked when the APP has been started.
How about the APP title?
Can we solve this problem before the APP start?

Let's check how should we do for iOS first....
Assume that we want to support English and Spanish.
We need to modify build.settings as follow:
iphone =
{
 plist =
 {
     CFBundleLocalizations={
            "en",
            "es",
      },
      CFBundleDisplayName = "myAppTitle",
      CFBundleName = "myAppTitle"",
    }
 }
After that, we need to add some folders for those languages we want to support.
For example, we will add folder "en.lproj" and "es.lproj" for our case.
In the folders we just added, we all need to add  one file "InfoPlist.strings",
In  "InfoPlist.strings", we need to add:
"CFBundleDisplayName"="myAppTitle";
"CFBundleName"="myAppTitle";
The above example is for "en.lproj".
Other languages are similar.

[Corona SDK] 如何讓您你的APP名稱也支援多國語言 - iOS篇

在[Corona SDK] 如何支援多國語言裡,
我們知道如何支援多國語言,
不過,裡面的方法是APP啓動之後,
那APP的名稱呢?
在APP啓動之前,我們如何解決這個問題呢?

我們先來看看iOS系統要如何處理....
假設我們要支援英文,繁體中文和簡體中文,
在build.settings裡我們先修改如下:
iphone =
{
 plist =
 {
     CFBundleLocalizations={
            "en",
            "zh-Hans",
            "zh-Hant",
      },
      CFBundleDisplayName = "myAppTitle",
      CFBundleName = "myAppTitle"",
    }
 }
然後,我們再根據想要支援的語言新增幾個目錄,
如以上範例,我們新增了"en.lproj","zh-Hant.lproj","zh-Hans.lproj",

如果要增加其它語言,方法類似,
在剛剛增加的目錄裡,我們都新增了一個檔案"InfoPlist.strings",
"InfoPlist.strings"裡的內容如下:
"CFBundleDisplayName"="myAppTitle";
"CFBundleName"="myAppTitle";
上面是"en.lproj"裡的,
如果是"zh-Hant.lproj"裡的"InfoPlist.strings",會是:
"CFBundleDisplayName"="我的名稱";
"CFBundleName"="我的名稱";
其它語言的方法類似

[Corona SDK] How to support multi-language (localize your APP)

If we only want to support one language, it will be quite simple for programming.
We can code it  as:
display.newText("hello", 200, 200, native.systemFontBold, 36 )

But, what if we want to support more than two languages?

First, we need to get the information of languages for current settings in user phones.
For Android system, we may use system.getPreference( "locale", "language" ) to get such information.
For iOS, we may use system.getPreference( "ui", "language" ).
However, it will be complicated for Chinese.
There have so-called Simplified Chinese and Traditional Chinese.
To overcome such problem, you can reference the following codes:
language = "en"
isAndroid = false
if(system.getInfo("platformName") == "Android") then
 isAndroid = true
end
if(isAndroid) then
 language = system.getPreference( "locale", "language" )
else
 language = system.getPreference( "ui", "language" )
end

local localeLanguage = system.getPreference( "locale", "language" ):upper()
local localeCountry = system.getPreference( "locale", "country" ):upper()
local uiLanguage = system.getPreference( "ui", "language" ):upper()

if(localeLanguage == "ZH-HANT" or localeLanguage == "ZH_HANT" or uiLanguage == "ZH-HANT" or uiLanguage == "ZH_HANT") then
 language = "zh-Hant"
elseif(localeLanguage == "ZH-HANS" or localeLanguage == "ZH_HANS" or uiLanguage == "ZH-HANS" or uiLanguage == "ZH_HANS") then
 language = "zh-Hans"
elseif(localeLanguage == "中文" or localeLanguage == "ZH" or uiLanguage == "中文" or uiLanguage == "ZH") then
 if(localeCountry == "TW") then
  language = "zh-Hant"
 else
  language = "zh-Hans"
 end
end
After getting the language setting, we need to take care of display problem.
We can add a new multi-language translation file, such as translations.lua as show below:
local translations =
{
    ["hello"] =
    {
        ["en"] = "hello",        
        ["zh-Hant"] = "哈囉",
        ["zh-Hans"] = "哈啰",
        ["fr"] = "Salut",
        ["de"] = "Hallo",

    },      
}

return translations
When we want to display the text, we can code it as:
translations = require("translations")
display.newText( translations["hello"][language], 200, 200, native.systemFontBold, 36 )