diff --git a/examples/cpp/PurchaseExample/ApplicationController.cpp b/examples/cpp/PurchaseExample/ApplicationController.cpp index 820b9f284..6872a5f5d 100644 --- a/examples/cpp/PurchaseExample/ApplicationController.cpp +++ b/examples/cpp/PurchaseExample/ApplicationController.cpp @@ -22,6 +22,7 @@ * @brief The controller that handles all purchase related * The controller is responsible for updating the UI with all * purchase events. + * @author Emma Tresanszki */ #include @@ -43,6 +44,7 @@ mMainScreen(NULL) // Set Android public key. PurchaseManager::getInstance()->setPublicKey(DEVELOPER_PUBLIC_KEY); + PurchaseManager::getInstance()->setStoreURL(sAppStoreSandboxURL); } /** diff --git a/examples/cpp/PurchaseExample/ApplicationController.h b/examples/cpp/PurchaseExample/ApplicationController.h index 8f04bf62f..2e6e923f3 100644 --- a/examples/cpp/PurchaseExample/ApplicationController.h +++ b/examples/cpp/PurchaseExample/ApplicationController.h @@ -22,6 +22,7 @@ * @brief The controller that handles all purchase related * The controller is responsible for updating the UI with all * purchase events. + * @author Emma Tresanszki */ #ifndef APPLICATIONCONTROLLER_H_ @@ -33,7 +34,6 @@ #include #include #include -#include #include using namespace NativeUI; diff --git a/examples/cpp/PurchaseExample/Main.cpp b/examples/cpp/PurchaseExample/Main.cpp index cf12368f3..56e36f55d 100644 --- a/examples/cpp/PurchaseExample/Main.cpp +++ b/examples/cpp/PurchaseExample/Main.cpp @@ -24,12 +24,12 @@ MA 02110-1301, USA. * * It is a basic example that demonstrates how to purchase * a product and how to get the receipt for it. + * @author Emma Tresanszki */ #include // Syscalls #include // C++ String class #include // Moblet class -#include // lprintfln for logging #include #include // Include all widgets diff --git a/examples/cpp/PurchaseExample/MainScreen.cpp b/examples/cpp/PurchaseExample/MainScreen.cpp index 0bb0de3fe..aa7fdb61d 100644 --- a/examples/cpp/PurchaseExample/MainScreen.cpp +++ b/examples/cpp/PurchaseExample/MainScreen.cpp @@ -23,7 +23,8 @@ * - "Buy" section with a buy button and a list of available items for sale. * - "History" section with a list of purchased items. * For each purchased item there is a Receipt button. - * When the Receipt button is pressed a dialog/new screen shows the receipt details. + * When the Receipt button is pressed a dialog shows the receipt details. + * @author Emma Tresanszki */ #include @@ -34,11 +35,12 @@ #include #include +#include + #include "Util.h" #include "MainScreen.h" #define BREAKLINE_HEIGHT 10 -#define ANDROID_PRODUCT_TYPE_PURCHASED "android.test.purchased" #define IOS_PRODUCT_TYPE_1 "com.mosync.purchase2.consumable" #define IOS_PRODUCT_TYPE_2 "com.mosync.purchase2.nonconsumable" @@ -189,9 +191,19 @@ void MainScreen::productPurchased(MAUtil::String productId) mBuyButton->setEnabled(true); ListViewItem* item = new ListViewItem(); - item->setText(productId); item->setFontColor(ITEMS_COLOR); mPurchasedItemsList->addChild(item); + + // Search the productId among the product list, + // and only display the product name. + for (int i=0; i < mProductIdList.size(); i++) + { + if ( strcmp(mProductIdList[i].c_str(), productId.c_str()) == 0 ) + { + item->setText(mProductNamesList[i]); + break; + } + } } /** @@ -303,12 +315,15 @@ void MainScreen::createProductIdList() * If you want to run the example for your own product ids, * add them to the mProductIdList list. */ - mProductIdList.add(ANDROID_PRODUCT_TYPE_PURCHASED); + mProductIdList.add(sGooglePlayPurchasedProductId); + mProductNamesList.add("Test product"); } else { mProductIdList.add(IOS_PRODUCT_TYPE_1); + mProductNamesList.add("Consumable product"); mProductIdList.add(IOS_PRODUCT_TYPE_2); + mProductNamesList.add("Non-consumable product"); } } @@ -332,7 +347,7 @@ void MainScreen::createMainLayout() buyLayout->addChild(info); // Add the list of available items for sale along with check boxes. - for (int i=0; i < mProductIdList.size(); i++) + for (int i=0; i < mProductNamesList.size(); i++) { HorizontalLayout* itemLayout = new HorizontalLayout(); itemLayout->wrapContentVertically(); @@ -340,7 +355,7 @@ void MainScreen::createMainLayout() itemLayout->addChild(itemCheckBox); mItemsCheckBoxes.add(itemCheckBox); Label* itemId = new Label(); - itemId->setText(mProductIdList[i]); + itemId->setText(mProductNamesList[i]); itemId->setFontColor(ITEMS_COLOR); itemLayout->addChild(itemId); buyLayout->addChild(itemLayout); diff --git a/examples/cpp/PurchaseExample/MainScreen.h b/examples/cpp/PurchaseExample/MainScreen.h index 13140e029..919b305f3 100644 --- a/examples/cpp/PurchaseExample/MainScreen.h +++ b/examples/cpp/PurchaseExample/MainScreen.h @@ -24,6 +24,7 @@ MA 02110-1301, USA. * - "History" section with a list of purchased items. * For each purchased item there is a Receipt button. * When the Receipt button is pressed a dialog/new screen shows the receipt details. + * @author Emma Tresanszki */ #ifndef MAINSCREEN_H_ @@ -123,15 +124,6 @@ class MainScreen: */ virtual void buttonClicked(Widget* button); -// /** -// * This method is called when a list view item is clicked. -// * @param listView The list view object that generated the event. -// * @param listViewItem The ListViewItem object that was clicked. -// */ -// void listViewItemClicked( -// ListView* listView, -// ListViewItem* listViewItem); - /** * This method is called when the state of the check box was changed * by the user. @@ -158,10 +150,16 @@ class MainScreen: */ void createReceiptDialog(); private: - // List of purchases. - //MAUtil::Vector mPurchases; + /** + * The productIds of the available items, exactly as they are + * going to be send to the server in the purchase request. + */ MAUtil::Vector mProductIdList; + /** + * The product names of the available items, user readable. + */ + MAUtil::Vector mProductNamesList; /** * Main layout. */ @@ -185,5 +183,4 @@ class MainScreen: Button* mReceiptOkButton; }; - #endif /* MAINSCREEN_H_ */ diff --git a/examples/cpp/PurchaseExample/Util.cpp b/examples/cpp/PurchaseExample/Util.cpp index cbdc8cb04..5b3392b21 100644 --- a/examples/cpp/PurchaseExample/Util.cpp +++ b/examples/cpp/PurchaseExample/Util.cpp @@ -23,14 +23,6 @@ MA 02110-1301, USA. * Utility functions for the application. */ - -#define IOS_PRODUCT_TYPE_1 "com.mosync.purchase2.consumable" -// This test app can be purchased multiple times. -#define ANDROID_PRODUCT_TYPE_PURCHASED "android.test.purchased" -#define ANDROID_PRODUCT_TYPE_CANCELLED "android.test.canceled" -#define ANDROID_PRODUCT_TYPE_UNAVAILABLE "android.test.item_unavailable" -#define IOS_PRODUCT_TYPE_2 "com.mosync.purchase2.nonconsumable" - #include "Util.h" diff --git a/libs/Purchase/Purchase.h b/libs/Purchase/Purchase.h index 1e70032ef..5da222bcd 100644 --- a/libs/Purchase/Purchase.h +++ b/libs/Purchase/Purchase.h @@ -50,7 +50,42 @@ namespace IAP class PurchaseManager; /** - *\brief The Purchase class wraps a product that can be buyed and also all + * GooglePlay reserved productID for testing static in-app billing response + * as if a purchase was made. + * (testing purpose). + */ + const MAUtil::String sGooglePlayPurchasedProductId = "android.test.purchased"; + + /** + * GooglePlay reserved productID for testing static in-app billing response + * as if a refund was received. + * When you make an in-app billing request with this product ID, Google Play + * responds as though the purchase was refunded. + * (testing purpose). + */ + const MAUtil::String sGooglePlayRefundedProductId = "android.test.refunded"; + + /** + * GooglePlay reserved productID for testing static in-app billing response + * as if an unavailable product tried to be purchased. + * When you make an in-app billing request with this product ID, Google Play + * responds as though the item being purchased was not listed in your + * application's product list. + * (testing purpose). + */ + const MAUtil::String sGooglePlayItemUnavailableProductId = "android.test.item_unavailable"; + + /** + * GooglePlay reserved productID for testing static in-app billing response + * as if a purchase was canceled. + * When you make an in-app billing request with this product ID Google Play + * responds as though the purchase was canceled. + * (testing purpose). + */ + const MAUtil::String sGooglePlayCanceledPurchaseProductId = "android.test.canceled"; + + /** + *\brief The Purchase class wraps a product that can be bought and also all * the details of the transaction(such as the receipt). */ class Purchase diff --git a/libs/Purchase/PurchaseManager.cpp b/libs/Purchase/PurchaseManager.cpp index 70177692f..f8b680c2e 100644 --- a/libs/Purchase/PurchaseManager.cpp +++ b/libs/Purchase/PurchaseManager.cpp @@ -107,10 +107,13 @@ namespace IAP * @param developerPublicKey Base64-encoded public key, that can be found * on the Google Play publisher account page, under Licensing & In-app * Billing panel in Edit Profile. - */ - void PurchaseManager::setPublicKey(const MAUtil::String& developerPublicKey) + * @return One of the next result codes: + * - MA_PURCHASE_RES_OK if the key was set. + * - MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY if the key is invalid. + */ + int PurchaseManager::setPublicKey(const MAUtil::String& developerPublicKey) { - maPurchaseSetPublicKey(developerPublicKey.c_str()); + return maPurchaseSetPublicKey(developerPublicKey.c_str()); } /** diff --git a/libs/Purchase/PurchaseManager.h b/libs/Purchase/PurchaseManager.h index 41c7f3165..047c9029f 100644 --- a/libs/Purchase/PurchaseManager.h +++ b/libs/Purchase/PurchaseManager.h @@ -96,8 +96,11 @@ namespace IAP * @param developerPublicKey Base64-encoded public key, that can be found * on the Google Play publisher account page, under Licensing & In-app * Billing panel in Edit Profile. - */ - void setPublicKey(const MAUtil::String& developerPublicKey); + * @return One of the next result codes: + * - MA_PURCHASE_RES_OK if the key was set. + * - MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY if the key is invalid. + */ + int setPublicKey(const MAUtil::String& developerPublicKey); /** * Set the store URL used for verifying the receipt on iOS platform. diff --git a/runtimes/cpp/platforms/android/IOCtl.cpp b/runtimes/cpp/platforms/android/IOCtl.cpp index fddb760b3..d9a520049 100644 --- a/runtimes/cpp/platforms/android/IOCtl.cpp +++ b/runtimes/cpp/platforms/android/IOCtl.cpp @@ -3409,18 +3409,18 @@ namespace Base { jstring jstrKey = jNIEnv->NewStringUTF(developerKey); jclass cls = jNIEnv->GetObjectClass(jThis); - jmethodID methodID = jNIEnv->GetMethodID(cls, "maPurchaseSetPublicKey", "(Ljava/lang/String;)V"); + jmethodID methodID = jNIEnv->GetMethodID(cls, "maPurchaseSetPublicKey", "(Ljava/lang/String;)I"); if (methodID == 0) { return 0; } - jNIEnv->CallVoidMethod(jThis, methodID, jstrKey); + int result = jNIEnv->CallIntMethod(jThis, methodID, jstrKey); jNIEnv->DeleteLocalRef(cls); jNIEnv->DeleteLocalRef(jstrKey); - return 1; + return result; } int _maPurchaseRequest(MAHandle productHandle, int quantity, JNIEnv* jNIEnv, jobject jThis) diff --git a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncPurchase.java b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncPurchase.java index 6807dcc5d..eb421e692 100644 --- a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncPurchase.java +++ b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncPurchase.java @@ -154,14 +154,17 @@ public void maPurchaseCreate(final int productHandle, final String productID) * Internal function for the maPurchaseSetPublicKey system call. * * @param developerPublicKey + * @return MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY or MA_PURCHASE_RES_OK. */ - public void maPurchaseSetPublicKey(String developerPublicKey) + public int maPurchaseSetPublicKey(String developerPublicKey) { panicIfBillingPermissionIsNotSet(); if ( mPurchaseManager != null ) { - mPurchaseManager.setKey(developerPublicKey); + return mPurchaseManager.setKey(developerPublicKey); } + Log.e("@@MoSync","maPurchaseSetPublicKey error: not available"); + return MA_PURCHASE_RES_UNAVAILABLE; } /** @@ -226,11 +229,8 @@ public int maPurchaseGetName(int productHandle, int memBuffer, int memBufSize) return result.length( ); } - else - { - Log.e("@@MoSync","maPurchaseGetName error: not available"); - return MA_PURCHASE_RES_UNAVAILABLE; - } + Log.e("@@MoSync","maPurchaseGetName error: not available"); + return MA_PURCHASE_RES_UNAVAILABLE; } /** @@ -284,11 +284,8 @@ public int maPurchaseGetField( return result.length( ); } - else - { - Log.e("@@MoSync","maPurchaseGetField error: not available"); - return MA_PURCHASE_RES_UNAVAILABLE; - } + Log.e("@@MoSync","maPurchaseGetField error: not available"); + return MA_PURCHASE_RES_UNAVAILABLE; } /** @@ -336,17 +333,13 @@ public void maPurchaseVerifyReceipt(int handle) */ public int maPurchaseDestroy(int handle) { -// panicIfBillingPermissionIsNotSet(); + panicIfBillingPermissionIsNotSet(); if (mPurchaseManager != null) { return mPurchaseManager.destroyPurchase(handle); - - } - else - { - Log.e("@@MoSync","maPurchaseDestroy error: not available"); - return MA_PURCHASE_RES_UNAVAILABLE; } + Log.e("@@MoSync","maPurchaseDestroy error: not available"); + return MA_PURCHASE_RES_UNAVAILABLE; } /************************ Class members ************************/ diff --git a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncThread.java b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncThread.java index 59ed949c4..69e21ee4b 100644 --- a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncThread.java +++ b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/MoSyncThread.java @@ -3714,9 +3714,9 @@ void maPurchaseCreate(final int productHandle, final String productID) * \param developerKey Base64-encoded public key, that can be found on the Google * Play publisher account page, under Licensing & In-app Billing panel in Edit Profile. */ - void maPurchaseSetPublicKey(final String developerPublicKey) + int maPurchaseSetPublicKey(final String developerPublicKey) { - mMoSyncPurchase.maPurchaseSetPublicKey(developerPublicKey); + return mMoSyncPurchase.maPurchaseSetPublicKey(developerPublicKey); } /** @@ -5280,48 +5280,89 @@ int maNFCIsReadOnly(int tagHandle) { return mMoSyncNFC == null ? IOCTL_UNAVAILABLE : mMoSyncNFC.maNFCIsReadOnly(tagHandle); } + /** + * Fill in an MACellInfo. + * @param cellinfo Pointer to an MACellInfo struct in MoSync memory. + * @return 0 on success, <0 on error. + * TODO: Should there be constants for error codes? Now they are + * undocumented (no documentation in maapi.idl). + */ int maGetCellInfo(int cellinfo) { // Check that the Coarse Location permission is set, - // otherwise the CellID request will freeze the device - if(!(mContext.getPackageManager().checkPermission("android.permission.ACCESS_COARSE_LOCATION", - mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED)) + // otherwise the CellID request will freeze the device. + int permission = mContext.getPackageManager().checkPermission( + "android.permission.ACCESS_COARSE_LOCATION", + mContext.getPackageName()); + if (permission != PackageManager.PERMISSION_GRANTED) { - return -3; + return -3; // Error (Permission not set). } TelephonyManager manager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + if (null == manager) + { + return -2; + } - // Only works with GSM type phone, no CDMA - if(manager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) + // Only works with GSM type phone, not with CDMA. + if (manager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) + { return -2; + } // Get the Cell information - GsmCellLocation gsmcell = (GsmCellLocation) manager.getCellLocation(); - - if(gsmcell == null) + GsmCellLocation gsmCell = (GsmCellLocation) manager.getCellLocation(); + if (null == gsmCell) + { return -2; + } - int cell = gsmcell.getCid(); - int lac = gsmcell.getLac(); + // Get cell id and lac. + int cellId = gsmCell.getCid(); + int lac = gsmCell.getLac(); - // Get the mcc and mnc strings - String mcc_mnc = manager.getNetworkOperator(); - if (mcc_mnc == null) + // Get MCC and NMC. + // MCC and NMC is concatenated in networkOperator. + // First 3 chars are MCC, remaining chars are MNC. + String mcc = ""; + String mnc = ""; + String networkOperator = manager.getNetworkOperator(); + if (null == networkOperator) + { return -2; + } - byte[] mcc = mcc_mnc.substring(0, 3).getBytes(); - byte[] mnc = mcc_mnc.substring(3).getBytes(); + if (networkOperator.length() > 4) + { + // Extract values. + mcc = networkOperator.substring(0, 3); + mnc = networkOperator.substring(3); - // Store everything in the correct memory location - ByteBuffer mem = getMemorySlice(cellinfo, 64); - mem.put(mcc); + // Guard against overflow. + if (mnc.length() > 7) + { + mnc = mnc.substring(0, 8); + } + } + + // Store everything in the correct memory location. + // Size of MACellInfo is 20 bytes. + ByteBuffer mem; + // Store MCC (max 4 bytes including terminator char). + mem = getMemorySlice(cellinfo, 4); + mem.put(mcc.getBytes()); mem.put((byte)0); - mem.put(mnc); + // Store MNC (max 8 bytes including terminator char). + mem = getMemorySlice(cellinfo + 4, 8); + mem.put(mnc.getBytes()); mem.put((byte)0); + + // Put lac and cellid, these are two 4 byte ints. + mem = getMemorySlice(cellinfo + 4 + 8, 8); mem.putInt(lac); - mem.putInt(cell); + mem.putInt(cellId); return 0; } diff --git a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/BillingService.java b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/BillingService.java index 299d9c0fe..cfc8d32d7 100644 --- a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/BillingService.java +++ b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/BillingService.java @@ -46,6 +46,8 @@ import static com.mosync.internal.generated.MAAPI_consts.MA_PURCHASE_STATE_FAILED; import static com.mosync.internal.generated.MAAPI_consts.MA_PURCHASE_STATE_COMPLETED; import static com.mosync.internal.generated.MAAPI_consts.MA_PURCHASE_STATE_PRODUCT_REFUNDED; +import static com.mosync.internal.generated.MAAPI_consts.MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY; +import static com.mosync.internal.generated.MAAPI_consts.MA_PURCHASE_RES_OK; import static com.mosync.internal.android.MoSyncHelpers.SYSLOG; /** @@ -85,17 +87,20 @@ public void setThread(MoSyncThread thread) /** * Compute your public key (that you got from the Android Market publisher site). * @param developerPublicKey the Base64 encoded key. + * @return MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY or MA_PURCHASE_RES_OK. */ - public void setPublicKey(final String developerPublicKey) + public int setPublicKey(final String developerPublicKey) { try { mPublicKey = Security.generatePublicKey(developerPublicKey); mIsPublicKeySet = true; + return MA_PURCHASE_RES_OK; } catch (Exception ex) { SYSLOG("maPurchaseSetPublicKey: malformed developerPublicKey"); mPublicKey = null; + return MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY; } } diff --git a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/PurchaseManager.java b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/PurchaseManager.java index 01c85bbda..9eb062b0e 100644 --- a/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/PurchaseManager.java +++ b/runtimes/java/platforms/androidJNI/AndroidProject/src/com/mosync/internal/android/billing/PurchaseManager.java @@ -318,9 +318,9 @@ public void verifyReceipt(int handle) * Generate a PublicKey instance from a string containing the * Base64-encoded public key. */ - public void setKey(final String developerPublicKey) + public int setKey(final String developerPublicKey) { - mService.setPublicKey(developerPublicKey); + return mService.setPublicKey(developerPublicKey); } public int destroyPurchase(int handle) diff --git a/tools/idl2/purchase.idl b/tools/idl2/purchase.idl index c08c97bc0..9477be153 100644 --- a/tools/idl2/purchase.idl +++ b/tools/idl2/purchase.idl @@ -60,6 +60,11 @@ group PurchaseResultCodes "Purchase Result Codes" { * contain the given field. */ INVALID_FIELD_NAME = -10; + /** + * \bried returned by the #maPurchaseSetPublicKey() syscall if the encoded public key + * is invalid. + */ + MALFORMED_PUBLIC_KEY = -11; } } // end of Purchase Result Codes @@ -399,8 +404,12 @@ group PurchaseFunctions "Purchase Functions" { * * \param developerPublicKey Base64-encoded public key, that can be found on the Google * Play publisher account page, under Licensing & In-app Billing panel in Edit Profile. + * + * \returns One of the next result codes: + * - #MA_PURCHASE_RES_OK if the key was set. + * - #MA_PURCHASE_RES_MALFORMED_PUBLIC_KEY if the key is invalid. */ - void maPurchaseSetPublicKey(in MAString developerPublicKey); + int maPurchaseSetPublicKey(in MAString developerPublicKey); /** * Request the user to purchase a product.