2015年7月28日 星期二

[C#] Android, iOS App icon generator

When we develop Android or iOS App, we need to design the icon the our App as well.
The annoying problem is that we need to have different resolutions of icons.

There have many free website which can help to us to do that.
However, some websites need to have account to start generating.
Some websites may have slow response time.

So...I decide to write a program using C# for such purpose.
At the same time, I think that there may have many people have similar requirement like me.
I would like to share my program as below:

I suggest that you can design a icon with the resolution of 1024x1024.
That you can open it with generateAppIcon and generate files with different resolutions you need.


By the way, we often use rounded corner in our icon.
In Android, you can design your rounded corner.
The system will show the icon as you design.

However, in iOS, the system will generate the rounded corner itself.
The radius of rounded corner may change for different system version.
I would like to suggest that you can design a icon without rounded corner and let the system to generate the rounded corner itself.

[C#] Android, iOS App icon產生器

當我們開發Android,iOS的App時,需要製作App的icon,
比較麻煩是,我們需要不同解析度的icon,
網路上有許多免費的網站可以幫忙產生不同的解析度,
不過,有的網站需要申請帳號,
有的網站處理速度很慢,

所以,只好自己用C#寫一個來用,
我想可能很多人都有這樣的需求,
所以就分享出來,
下載網址如下:

建議先製作一個1024x1024大小的icon,
然後丟到generateAppIcon去產生不同的解析度,
Launch Images是app還沒開始前會先載入的畫面,
很多人會放公司的logo之類的,
它的大小最大是1242x2208, 1536x2048等等,
最好是製作一個高解析的圖當成原圖,

附帶一提,
很多App icon都會有圓角的設計,
在Android,你可以設計自己想要的圓角,
它會按照你產生的icon來顯示,

在iOS,系統會自己截取你的icon來產生圓角,
而且它的圓角也可能因為不同版本會改變,
除非你的icon和圓角半徑有關,
不然,建議你可以產生一個沒有圓角的icon,
讓系統自己來產生圓角

[C#] When we use PictureBox to load the picture file,the picture file will be locked?

We often use PictureBox to load the picture file, like below:
try
{
 var srcImage = Image.FromFile("test.jpg");
 myPictureBox.Image = srcImage;
}
catch (System.OutOfMemoryException)
{
 MessageBox.Show("Invalid picture format.");
}
However, you will find that you cannot move or rename this picture file.
That is this picture file is locked.

What if we wish that it will not be locked?
We can solve this problem be using FileStream, as below:
try
{
 FileStream stream = new FileStream(openFileDialog1.FileName, FileMode.Open);
 var srcImage = Image.FromStream(stream);
 stream.Close();
 myPictureBox.Image = srcImage;
}
catch (System.ArgumentException)
{
 MessageBox.Show("Invalid picture format.");
}

[C#] 圖片檔案載入PictureBox 控制項時,影像檔案會被鎖住?

我們常常會利用PictureBox來載入影像檔案,
例如以下的用法:
try
{
 var srcImage = Image.FromFile("test.jpg");
 myPictureBox.Image = srcImage;
}
catch (System.OutOfMemoryException)
{
 MessageBox.Show("Invalid picture format.");
}
不過,你會發現此時你無法再對這個做移動或者更名等動作,
因為這個檔案已經被鎖住了,

如果我們不希望它被鎖住呢?
我們可以藉用FileStream來解決,如下:
try
{
 FileStream stream = new FileStream(openFileDialog1.FileName, FileMode.Open);
 var srcImage = Image.FromStream(stream);
 stream.Close();
 myPictureBox.Image = srcImage;
}
catch (System.ArgumentException)
{
 MessageBox.Show("Invalid picture format.");
}

2015年6月21日 星期日

[遊戲] 迷宮賽車

Android Play Store:

iOS App Store:

一個結合速度和策略的全新型態遊戲,而且全部免費
高達80個免費關卡等你來挑戰.
只要滑動手指就能享受樂趣.
需要收集所有的旗子. 但是,過程中一直會有瘋狂火箭追逐著你.
當你改變車道時,要小心已經有炸彈在那等著你.
每關的挑戰都不同,你必須快速的做出反應,而且需要聰明的策略來通過那些障礙物.
移動炸彈,從天而降的飛碟,無敵的坦克...等等,你永遠不知道下一關又會跑出什麼新變化.





[Games] Crazy Maze Racing

Android Play Store:

iOS App Store:

A game combines speed with strategy.
Up to 80 rounds for you to challenge, and all are free.
Just slide your finger to control the car. 
Need to collect all flags within limited time. 
In the meanwhile, there always have crazy rockets chasing you.
Be careful of bombs when you change the lane.
Every round has various challenges. 
You need to respond quickly with smart strategy to get through obstacles in your way.
Walking bombs, fly UFO, unbeatable tanks..., you can never expect what will come out for next round.





2015年6月16日 星期二

[Corona SDK] How to implement In-App Purchases (IAP)?

It seems that In-App Purchases (IAP) feature is more popular nowadays.
That is, the App itself is free. 
However, if you want to get no-ads version, get all rounds, add some features or retrieve some weapons and so on, you will need to pay then.
This is what called In-App Purchases (IAP).

Below, we will consider about Android and iOS both.
Let's check the common codes shown below first, and discuss them later:
local store
local currentProductList = nil

local appleProductList = {
    {"com.gmail.myTest.DemoApp.GetFullVersion"}
}

local googleProductList = {
    "com.gmail.mytest.demo_app.getfullversion",
}
local function storeTransaction( event )
    local transaction = event.transaction

    if ( transaction.state == "purchased" ) then
     print("transaction.state == purchased")
        if(fullVersion == false) then
         fullVersion = true
     end
    elseif ( transaction.state == "restored" ) then
     --iOS only
     print("transaction.state == restored")
        print( "productIdentifier:", transaction.productIdentifier )
        print( "originalReceipt:", transaction.originalReceipt )
        print( "originalTransactionIdentifier:", transaction.originalIdentifier )
        print( "originalDate:", transaction.originalDate )
       if(fullVersion == false) then
         fullVersion = true
     end
    elseif ( transaction.state == "refunded" ) then
     --refunds are only supported by the Android
     print("transaction.state == refunded")        
        print( "productIdentifier:", transaction.productIdentifier )
        if(fullVersion == true) then
         fullVersion = false
     end
    elseif ( transaction.state == "consumed" ) then
     --consumed are only supported by the Android
     print("transaction.state == consumed")        
        print( "productIdentifier:", transaction.productIdentifier )
  if(fullVersion == true) then
   fullVersion = false
  end
  elseif event.transaction.state == "cancelled" then
   --For iOS, if user cancels the IAP action, it will come to here 
   print("transaction.state == cancelled")
  elseif event.transaction.state == "failed" then 
   --[[
   -1003: unknown
   -1005: user canclled,For Android, if user cancels the IAP action, it will come to here
  -7: already owned
  8: item not owned
   ]]
   print("transaction.state == failed")
   print("Transaction failed, type:"..tostring(event.transaction.errorType)..",-1005")
   print("Transaction failed, error:"..tostring(event.transaction.errorString)) 
    end    

    store.finishTransaction( event.transaction )
end


if ( isAndroid ) then
    store = require( "plugin.google.iap.v3" )
    currentProductList = googleProductList
    store.init( "google", storeTransaction )     
else
    store = require( "store" )
    currentProductList = appleProductList
    store.init( "apple", storeTransaction )
end 

--put the following codes somewhere, such as when user press some button 
if(store.canMakePurchases) then
 store.purchase( currentProductList[1] )
end

Android
First, you need to add your App in Play Store Publish.
After that, enter the need added App and then add the In-App products.
There are three types of IAP products for google IAP: managed, unmanaged and subscription.
Managed: It is for one-time shopping, e.g. changed from lite version to full version.
For this kind of products, Google will store the purchase information.
If user change phones or re-install the App, they can restore those managed products from Google server if they are using the same accounts.
Unmanaged: It is for those products which can be purchased again and again, e.g. weapons or blood
values of roles.
The Google server will not store the purchases records.
That is, the developers need to have some ways to store such information themselves.
Subscription: Let's not discuss it currently.

After that, we need to fill the IAP item id in googleProductList, as shown in above codes.

In Play Store Publish, go to the "Services & APIs" page and get the license key for this application.
It is a very long string. Just copy it and paste to the key field in config.lua:
application = 
{
 content = 
 { 
  width = 640,
  height = 960, 
  scale = "zoomStretch",
 },
 license =
    {
        google =
        {
            key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        },
    },
}


Next, we need to add the plugins and permissions in build.settings:
settings =
{
 plugins =
    {
        ["plugin.google.iap.v3"] =
        {
            publisherId = "com.coronalabs",
            supportedPlatforms = { android=true }
        },        
    },
 android =
    {
        usesPermissions =
        {
            "com.android.vending.BILLING",
        },
    },
}

Purchase
To purchase, we only need to call store.purchase( currentProductList[1] ).
However, we need to upload the built apk file to Play Store Publish first.
The version code of  uploaded apk file should be the same as what apk you want to test.
You can modify your codes and build many times, just remember to use the same version code.
We can upload to ALPHA testing or BETA testing.
If there is no apk file with same version code in Play Store Publish, you will get error message telling that the IAP function is not provided in this version.
In above codes, we check store.canMakePurchases before calling store.purchase( currentProductList[1] ). 
store.canMakePurchases is for iOS only. It will be true always for Android platform.

After purchasing successfully, the callback function storeTransaction will enter transaction.state == "purchased".
We can modify the related authorization there.

Test
We are not allow to test IAP function with the same account.
We need to get another account for testing.
If the account in your phone is the same as your developer account, you need to back to factory default and set new account.

We can go to Play Store Publish, find the setting page and then add the test account.
When you purchase your IAP products with this testing account, it will not spend you any money.

During our test, we are not sure if the problems are caused by wrong coding, or by wrong setting in Play Store Publish.
Google suggest us that we should do what called "Static Responses" test first.
That is, we can replace the items in googleProductList to be following items:
"android.test.purchased"
"android.test.canceled"
"android.test.refunded"
"android.test.item_unavailable"
These items are defined by Google.
We don't need to set anything IAP items in Play Store Publish in advance.
We don't need to upload our apk file either.
We'd better use them to test our IAP related codes first.

Refund
Google allow user to perform refund after purchasing.
Basically, we just need change the related authorization in  transaction.state == "refunded" of callback function storeTransaction.

Comsuming items
This is Google specific feature.
If your item is managed type, you may need to re-purchase after you modify some error codes.
We can consume the purchased record in Google server:
store.consumePurchase( currentProductList[1], storeTransaction )
The callback function storeTransaction should enter transaction.state == "consumed" then.
After that, we can perform store.purchase( currentProductList[1] ) again.
If you don't do store.consumePurchase(), you will get the error message in transaction.state == "failed", which tells you that the item is already owned.

Restore
If users have ever bought the managed products, they may install your App in different devices or re-install your App with the same account.
We need to restore the related authorization by following method:
store.restore( )
After calling it, the callback function storeTransaction will enter transaction.state == "purchased".
Google does not ask when or how we should do restore.
We can perform restore automatically or add a restore button.
If user did not buy it before, nothing will happen after calling store.restore( ) .
As mentioned above, doing purchase again will get error message "already owned", instead of entering transaction.state == "purchased" or transaction.state == "restored" 
We have to call store.restore( ) somewhere.

iOS
You have to visit iTunes Connect to add your new App.
We can add IAP products after adding new App successfully.
There are five types of IAP products provide by Apple.
Consumable products are similar with unmanaged products mentioned above.
Non-Consumable products are similar with managed products mentioned above.

Similarly, we need to fill the IAP item id in appleProductList of our codes.
Note that appleProductList is in table format, which is different from googleProductList.

Purchase
To purchase, we need call store.purchase( currentProductList[1] ).
We have to check store.canMakePurchases because iOS devices have a setting that disables purchasing.
After purchasing successfully, the callback function storeTransaction will enter transaction.state == "purchased".
We can modify the related authorization there.

Test
Our App has not been published to Store.
When we do IAP test, we will get the error message telling that the App cannot connect with iTunes Store.
To test it, we need to setup the sandbox environment.
Visit iTunes Connect, go to the "Manage Users" page and add the test counts.
The added accounts cannot be any accounts which have already existed.
That is, you can use those accounts in your phones or tablets.
We have to create a new Apple id for testing.
A suggested way is to combine your original account with test1, test2, and so on.
For example, if your phone Apple id is "ABC", then you can add test account "ABC+test1", "ABC+test2"....
Apple does not offer the function for eliminating purchased record, like store.consumePurchase() for Android we mentioned before.
If your products are non-consumable products, you will need to create many test accounts.
For Apple, if we do store.purchase() for the item which has been purchased, it will tell you that this item has been purchased and you can get it free for one more time.
It will not return error message as Google platform.
After purchasing, the callback function storeTransaction will enter transaction.state == "purchased" as well.
Anyway, we'd better test with new account which has never bought any items before.

Restore
If users have ever bought the managed products, they may install your App in different devices or re-install your App with the same account.
We need to restore the related authorization by following method:
store.restore( )
After calling it, the callback function storeTransaction will enter transaction.state == "restored".
Apple ask that if your App has non-consumable products inside, then you need to provide the restore button.
That is, we should perform restore after user triggering it.
We cannot restore it automatically.
Why?
Because if we perform restore automatically, it will pop up the window for asking password.
Users may feel strange for showing up such menu automatically.
As we mentioned in last paragraph, if we perform purchase for the purchased products, we can restore the purchased items too. It is free.
Why do we need the restore button? 
Can we just ask user to press the purchase button again?
We do need the restore button.
Users may feel that they need to pay again for pressing purchase button.
The message about getting the products again for free is shown up after you press the purchase button.


For Android Play Store Publish or iOS iTunes Connect, you need to wait for a while to take effect after you set up a new IAP product.
It may take few hours sometimes.