I got a code from internet for inapp billing and I want to use that code in my application but I am getting an error that when I click the the buy button of my app it redirect me to the another layout of the code where I get an another Button and after that click my in-app billing starts.
I want that when I click my buy button then the in-app billing should start. without any another button clicks.
This is the code from where the the in-app billing start.
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSP = PreferenceManager.getDefaultSharedPreferences(this);
Log.i("BillingService", "Starting");
setContentView(R.layout.contact_market);
mContext = this;
mPurchaseButton = (Button) findViewById(R.id.main_purchase_yes);
mPurchaseButton.setOnClickListener(this);
mPreview = (TextView) findViewById(R.id.chakkde);
startService(new Intent(mContext, BillingService.class));
BillingHelper.setCompletedHandler(mTransactionHandler);
}
public Handler mTransactionHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.i(TAG, "Transaction complete");
Log.i(TAG, "Transaction status: "
+ BillingHelper.latestPurchase.purchaseState);
Log.i(TAG, "Item purchased is: "
+ BillingHelper.latestPurchase.productId);
if (BillingHelper.latestPurchase.isPurchased()) {
showItem();
}
};
};
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.main_purchase_yes:
if (BillingHelper.isBillingSupported()) {
BillingHelper.requestPurchase(mContext,
"android.test.purchased");
} else {
Log.i(TAG, "Can't purchase on this device");
mPurchaseButton.setEnabled(false);
}
break;
default:
Log.i(TAG, "default. ID: " + v.getId());
break;
}
}
private void showItem() {
mPreview.setVisibility(View.VISIBLE);
SharedPreferences.Editor prefEditor = mSP.edit();
prefEditor.putBoolean(DroidSugarPreference.KEY_ENABLE,
true);
prefEditor.commit();
startActivity(new Intent(InAppMain.this, Setup.class));
InAppMain.this.finish();
}
#Override
protected void onPause() {
Log.i(TAG, "onPause())");
super.onPause();
}
#Override
protected void onDestroy() {
BillingHelper.stopService();
super.onDestroy();
}
}
this if from where I call the above class
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.gtask_button:
startActivity(new Intent(getActivity(), InAppMain.class));
default:
break;
}
but now i want that from case R.id.gtask_button: i should start the in-app billing activity that i was starting from R.id.main_purchase_yes.
thnx in advance...
From what i see, this is called when you click the button
BillingHelper.requestPurchase(mContext, "android.test.purchased");
So maybe thats where it changes your layout to something else...
Post the method so we can take a look.
EDIT:
Ok, here's the code
protected static void requestPurchase(Context activityContext, String itemId){
if (amIDead()) {
return;
}
Log.i(TAG, "requestPurchase()");
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString("ITEM_ID", itemId);
try {
Bundle response = mService.sendBillingRequest(request);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
//The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI
PendingIntent pendingIntent = (PendingIntent) response.get("PURCHASE_INTENT");
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "REQUEST_PURCHASE Sync Response code: "+responseCode.toString());
startBuyPageActivity(pendingIntent, new Intent(), activityContext);
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: "+isBillingSupported());
}
}
and we find the culprit -
startBuyPageActivity(pendingIntent, new Intent(), activityContext);
Related
I want to create a simple app which is intend to call a API in POST method when a button clicks.The app workflow will be like this; user enter the url and a timeout value, and press start button. The API will called in the provided url and it will again and again call according to the timeout value provided. When user press the stop button,all activity should stop.The API call will not provide any data in response. I simply want to call that API in POST method.In order to achieve this I written the API call in a service.
The problem Iam facing is
The POST method doesn't working.
2.How can I call this api in background according to the timeout value provided.?
What I have Done
My MainActivity
url = (EditText)findViewById(R.id.editText3);
timeOut = (EditText)findViewById(R.id.editText5);
Button clickButton = (Button) findViewById(R.id.start);
clickButton.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(MainActivity.this, APIService.class);
Bundle bund = new Bundle();
bund.putString("url",url.getText().toString());
bund.putString("timeout",timeOut.getText().toString());
intent.putExtras(bund);
startService(intent);
}
});
Button stopbutton = (Button) findViewById(R.id.stop);
stopbutton.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(MainActivity.this, APIService.class);
stopService(intent);
}
});
My API Service class
public APIService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
Toast.makeText(this, " Client API Service Started", Toast.LENGTH_LONG).show();
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Client API Service Started", Toast.LENGTH_LONG).show();
String WEBURL;
String TimeOut;
Bundle bund = intent.getExtras();
WEBURL=bund.getString("url");
TimeOut=bund.getString("timeout");
String URL= "http://"+WEBURL+"/API/ReportSubscription/GetAllReportSubscription?subscriptionUR="+WEBURL;
new HttpRequestTask(
new HttpRequest(URL, HttpRequest.POST),
new HttpRequest.Handler() {
#Override
public void response(HttpResponse response) {
if (response.code == 200) {
Log.d(this.getClass().toString(), "Request successful!");
} else {
Log.e(this.getClass().toString(), "Request unsuccessful: " + response);
}
}
}).execute();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Client API Service Stopped", Toast.LENGTH_LONG).show();
}
}
I Used compile 'com.apptakk.http_request:http-request:0.1.2' for Web API call.Any help is appriciated
You can use OkHttp for this. See this tutorial for more: https://www.vogella.com/tutorials/JavaLibrary-OkHttp/article.html
// avoid creating several instances, should be singleon
OkHttpClient client = new OkHttpClient();
String weburl = bund.getString("url");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("subscriptionUR", weburl)
.build();
Request request = new Request.Builder()
.url("http://"+weburl+"/API/ReportSubscription/GetAllReportSubscription")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Do something when request failed
e.printStackTrace();
Log.d(TAG, "Request Failed.");
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if(!response.isSuccessful()){
throw new IOException("Error : " + response);
}else {
Log.d(TAG,"Request Successful.");
}
// Read data in the worker thread
final String data = response.body().string();
}
});
First make sure your API is working by testing through Postman. After you have make sure that API is working fine from the backend itself then the problem is from your mobile side calling that API. I would really recommend retrofit (https://square.github.io/retrofit/) to try out API calling task asynchronously because its really easy and standard way .You simply using POST method in api by passing your time out value as a parameter and your api url as base url.
You can use service in android to execute in the background i.e your API calling
I'm having an issue with my apps in-app billing. I thought it was working well for the last week, but I'm having an unexpected result.
I have about 10 items for sale. Each item sets a shared pref value to true when purchased/querying inventory if it is bought. One item, is a "Buy All" button, when bought, it's suppose to set the values for all the others to true. This was working well, the problem arises when I add new items to buy. The "buy all" should give access to those as well, but it seems it's not.
I'll try to make my code as simple as possible while still showing the needed info:
BaseActivity.java(where all in-app purchases set up):
//SKU FOR our products
static final String SKU_W31 = "workout_31";
static final String SKU_W32 = "workout_32";
static final String SKU_W37 = "workout_37";
static final String SKU_ALL = "all_workouts";
//is paid?
public boolean m31paid = false;
public boolean m32paid = false;
public boolean m37paid = false;
public boolean mAllPaid = false;
IabHelper mHelper;
IabBroadcastReceiver mBroadcastReceiver;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences pref = getApplicationContext().getSharedPreferences("MyPref", 0);
mHelper = new IabHelper(this, base64EncodedPublicKey);
//SET FALSE FOR LIVE APP
mHelper.enableDebugLogging(false);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(LOG, "Problem setting up in app billing: " + result);
} else Log.d(LOG, "set up correctly!");
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(BaseActivity.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(LOG, "Setup successful. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
}
});
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(LOG, "Query inventory finished.");
SharedPreferences.Editor editor = getSharedPreferences("my_pref", MODE_PRIVATE).edit();
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
Purchase w37Purchase = inventory.getPurchase(SKU_W37);
m37paid = (w37Purchase != null && verifyDeveloperPayload(w37Purchase));
Log.d(LOG, "User has workout 37" + (m37paid ? "BOUGHT" : "NOT BOUGHT"));
if (w37Purchase != null) {
editor.putBoolean("workout37", true);
editor.apply();
}
Purchase w32Purchase = inventory.getPurchase(SKU_W32);
m32paid = (w32Purchase != null && verifyDeveloperPayload(w32Purchase));
Log.d(LOG, "User has workout 32" + (m32paid ? "BOUGHT" : "NOT BOUGHT"));
if (w32Purchase != null) {
editor.putBoolean("workout32", true);
editor.apply();
}
Purchase w31Purchase = inventory.getPurchase(SKU_W31);
m31paid = (w31Purchase != null && verifyDeveloperPayload(w31Purchase));
Log.d(LOG, "User has workout 31" + (m31paid ? "BOUGHT" : "NOT BOUGHT"));
if (w31Purchase != null) {
editor.putBoolean("workout31", true);
editor.apply();
}
Purchase wAllPurchase = inventory.getPurchase(SKU_ALL);
mAllPaid = (wAllPurchase != null && verifyDeveloperPayload(wAllPurchase));
Log.d(LOG, "User has " + (mAllPaid ? "BOUGHT" : "NOT BOUGHT"));
if (wAllPurchase != null) {
editor.putBoolean("workout31", true);
editor.putBoolean("workout32", true);
editor.putBoolean("workout37", true);
editor.apply();
}
}};
I then have the methods for buying that I put in the onClick of the corresponding buttons:
public void onBuy31ButtonClicked (View arg0) {
Log.d(LOG, "Buy 31 button clicked.");
if (m31paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 31");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W31, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuy32ButtonClicked (View arg0) {
Log.d(LOG, "Buy 32 button clicked.");
if (m32paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 32");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W32, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuy37ButtonClicked (View arg0) {
Log.d(LOG, "Buy 37 button clicked.");
if (m37paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 37");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W37, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuyAllButtonClicked (View arg0) {
Log.d(LOG, "Buy all button clicked.");
if (m32paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for all");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_ALL, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
My mPurchaseFinishedListener:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(LOG, "Purchase finished: " + result + ", purchase: " + purchase);
SharedPreferences.Editor editor = getSharedPreferences("my_pref", MODE_PRIVATE).edit();
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
if (purchase.getSku().equals(SKU_W30)) {
editor.putBoolean("workout30", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W31)) {
editor.putBoolean("workout31", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W32)) {
editor.putBoolean("workout32", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W37)) {
editor.putBoolean("workout37", true);
editor.apply();
return;
if(purchase.getSku().equals(SKU_ALL)) {
editor.putBoolean("workout31", true);
editor.putBoolean("workout32", true);
editor.putBoolean("workout37", true);
editor.apply();
return;
}
Then where the data is, I simply have an if statement to check the boolean value as such:
xpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
SharedPreferences pref = getApplicationContext().getSharedPreferences("my_pref", MODE_PRIVATE);
boolean w31 = pref.getBoolean("workout31", false);
boolean w32 = pref.getBoolean("workout32", false);
boolean w37 = pref.getBoolean("workout37", false);
if (groupPosition == 2) {
if(w31 == true) {
if (childPosition == 0) {
Intent intent = new Intent(getApplicationContext(), WorkoutDaysActivity.class);
intent.putExtra("workout", "w31w1");
startActivity(intent);
}
if (childPosition == 1) {
Intent intent = new Intent(getApplicationContext(), WorkoutDaysActivity.class);
intent.putExtra("workout", "w31w2");
startActivity(intent);
}
}else Toast.makeText(getApplicationContext(), "Sorry, but you need to purchase these workouts from the menu.", Toast.LENGTH_LONG).show();
}
All child items have the same code as above, switching out the w31 with w32 and w37.
I have taken out most purchases to try to cut back on code and still get the point across, but basically what is happening is 31 and 32 were added before, and then I used the purchase all button, they work. But I added 37 in a later update, and my theory was that the boolean value would change for it when it queries for all workouts and see it was purchased. In reality though, when I click 37 in the expandable list view, I get the toast saying it needs to be purchased, and when I go to the purchase page and click "Purchase All", I get the toast saying it was already purchased.
Does anyone see anything wrong with my code? Thanks a lot, this is causing huge problems!
Cause of async operations you may have a race condition. Your activity with expandable list view should be registered to all changes in your SharedPreferences.
I am pulling my hair out! At one point in the last week, I had this working.
I have an Android app that I am trying to add in-ap billing to. I followed the sample TrivialDrive, and my code worked a few times. Now it doesn't.
I am creating a simple trivia game that has a number of free questions, and the option to upgrade to get more questions. When the user completes the list of free questions, they are taken to a "Game Over" screen where they can erase their answers and start again, or upgrade.
When I click the "Upgrade" button, I can make a successful purchase, but as soon as the Google "Payment Successful" dialog goes away, my activity is destroyed and I am sent back to my main activity.
When I try to go back and do my purchase again, my code catches the error ("You already own this item") and handles it appropriately. My code explains to the user that they already own the upgrade, and allows them to click a button to continue playing. So it looks like the OnIabPurchaseFinishedListener is firing at this point.
I have updated the Google helper code with the latest files.
Any help or suggestions as to where to look for answers is much appreciated.
Thanks.
This is the relevant code for my activity:
public class GameOverActivity extends BaseActivity
{
private IabHelper mHelper;
private String m_base64EncodedPublicKey;
private static String THE_UPGRADE_SKU = "upgrade52";
public static int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_over);
setTitle("Game Over");
Button butPlay = (Button) findViewById(R.id.buttonPlay);
butPlay.setVisibility(View.INVISIBLE);
PrepareIAB();
}
#Override
protected void onResume()
{
super.onResume();
CURRENT_ACTIVITY = ACTIVITY_GAME_OVER;
SetMainText();
}
#Override
protected void onDestroy()
{
super.onDestroy();
try
{
if (mHelper != null)
{
mHelper.dispose();
mHelper = null;
}
}
catch (Exception e)
{
}
}
private void PrepareIAB()
{
m_base64EncodedPublicKey = "MyKey";
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, m_base64EncodedPublicKey);
mHelper.enableDebugLogging( true, TAG);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess())
{
ShowMessage("There was an error connecting to the Google Play Store.");
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
try
{
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data))
{
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
}
else
{
// Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
catch (Exception e)
{
super.onActivityResult(requestCode, resultCode, data);
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
try
{
if (result.isFailure())
{
if (result.mResponse==7)
{
UpgradeComplete();
ShowMessage("Thank you for upgrading.\r\n\r\nThis version has 400 more questions.");
}
else
{
ShowMessage("Error purchasing: " + String.valueOf(result.mResponse));
UpgradeError();
return;
}
}
else if (purchase.getSku().equals(THE_UPGRADE_SKU))
{
UpgradeComplete();
ShowMessage("Thank you for upgrading.\r\n\r\nThis version has 400 more questions.");
}
else
{
ShowMessage("Something else happened. ");
}
}
catch (Exception e)
{
Log.e(TAG, e.getLocalizedMessage());
}
}
};
private void HideUpgrade()
{
try
{
Button btnUpgrade = (Button) findViewById(R.id.buttonUpgrade);
if (btnUpgrade != null)
{
btnUpgrade.setVisibility(View.INVISIBLE);
}
TextView txtMessage = (TextView) findViewById(R.id.txtUpgradeFromGameOver);
if (txtMessage!=null)
{
txtMessage.setVisibility(View.INVISIBLE);
}
}
catch (Exception e)
{
}
}
public void onQuitButtonClick(View view)
{
finish();
}
public void onResetDBButtonClick(View view)
{
ConfirmResetDatabase();
}
private void ConfirmResetDatabase()
{
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
switch (which)
{
case DialogInterface.BUTTON_POSITIVE:
ResetDatabase();
Intent gameActivity = new Intent(getApplicationContext(), GameActivity.class);
gameActivity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
// startActivityForResult(gameActivity, ACTIVITY_GAME);
startActivity(gameActivity);
break;
case DialogInterface.BUTTON_NEGATIVE:
// No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Do you want to erase your score and start over?").setPositiveButton("Yes", dialogClickListener).setNegativeButton("No", dialogClickListener).show();
}
public void onUpgradeButtonClick(View view)
{
try
{
if (mHelper != null)
{
mHelper.launchPurchaseFlow(this, THE_UPGRADE_SKU, 10001, mPurchaseFinishedListener, m_TriviaAppInstance.AppInstallID());
}
else
{
ShowMessage("Unable to connect to Google Play Store.");
}
}
catch (Exception e)
{
ShowMessage("Unable to connect to Google Play Store.");
SendErrorMessage(e.getLocalizedMessage());
}
}
private void UpgradeComplete()
{
try
{
HideUpgrade();
Button butPlay = (Button) findViewById(R.id.buttonPlay);
if (butPlay!=null)
{
butPlay.setVisibility(View.VISIBLE);
}
TextView txtReset = (TextView) findViewById(R.id.txtGameOverRestDB);
if (txtReset!=null)
{
txtReset.setVisibility(View.INVISIBLE);
}
Button btnReset = (Button)findViewById(R.id.buttonResetDB);
if (btnReset!=null)
{
btnReset.setVisibility(View.INVISIBLE);
}
m_TriviaAppInstance.SetUpgradedStatus(true);
}
catch (Exception e)
{
}
//
}
private void UpgradeError()
{
try
{
Button butUpgrade;
butUpgrade = (Button) findViewById(R.id.buttonUpgrade);
butUpgrade.setVisibility(View.INVISIBLE);
TextView txtMessage = (TextView) findViewById(R.id.txtUpgradeScreen);
txtMessage.setText(R.string.upgradeScreenTextError);
}
catch (Exception e)
{
}
}
public void onPlayButtonClick(View view)
{
Intent myIntent = new Intent(view.getContext(), GameActivity.class);
myIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivityForResult(myIntent, ACTIVITY_GAME);
}
public void SetMainText()
{
TextView txt = (TextView) findViewById(R.id.txtScoreGlobal);
txt.setText(Integer.toString(m_TriviaAppInstance.getGlobal()) + "%");
SetPlayerScore(1);
if (m_TriviaAppInstance.getUpgradedStatus() == true)
{
HideUpgrade();
}
}
}
FYI: I think I have this figured out - for anyone else that may come across it.
The activity that I was using to launch "In App Billing" was called with a "FLAG_ACTIVITY_NO_HISTORY". I did this because I didn't want the user to be able to click to go back to this "Game Over" activity.
BUT, this causes grief with "In App Billing". So, make sure you don't try to launch "In App Billing" from an activity that has had the "FLAG_ACTIVITY_NO_HISTORY" set.
My original code:
private void GameOver()
{
m_TriviaAppInstance.setGameOver(true);
Intent gameOver = new Intent(getApplicationContext(), GameOverActivity.class);
gameOver.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(gameOver);
}
Updated code:
private void GameOver()
{
m_TriviaAppInstance.setGameOver(true);
Intent gameOver = new Intent(getApplicationContext(), GameOverActivity.class);
startActivity(gameOver);
}
Peace
I'm not high enough to comment, but bless you. I had
android:noHistory="true"
set in AndroidManifest.xml for my activity and was experiencing the same problem.
Took it out and IAB is working. Yay!
Do not forget that your IabHelper.OnIabPurchaseFinishedListener is called on a different thread and before onResume() is called on your Activity!
So your UpgradeComplete() or UpgradeError() can cause a crash on older devices (Crashed every time on my Gingerbread Sony Xperia Mini Pro and worked without any trouble on Samsung Galaxy S4 (Android 4.2.2)
Caused a 3 day delay on my game..
I've wrote an in app purchase test app to learn how to implement it in an application i've made.
I adapted the code from the TrivialDrive sample provided by google.
But it doesn't work, after my friend makes the payment the app crashes.
The code looks like this
public class MainActivity extends Activity {
String TAG = "AppPurchaseTest";
IabHelper mHelper;
boolean mIsPremium = false;
static final String SKU_PREMIUM = "premium";
static final int RC_REQUEST = 10001;
// User clicked the "Upgrade to Premium" button.
public void onUpgradeAppButtonClicked(View arg0) {
Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
// setWaitScreen(true);
mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener);
}
//this is not working
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
int duration = Toast.LENGTH_SHORT;
if (result.isFailure()) {
// Oh noes!
// complain("Error purchasing: " + result);
// setWaitScreen(false);
Toast.makeText(getBaseContext(), "Fail :(", duration).show();
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
// alert("Thank you for upgrading to premium!");
mIsPremium = true;
Toast.makeText(getBaseContext(), "Successo: adesso sei premium", duration).show();
Button test = (Button) findViewById(R.id.test);
test.setVisibility(View.INVISIBLE);
// updateUi();
// setWaitScreen(false);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String base64EncodedPublicKey = null;
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mHelper != null) {
Log.d(TAG, "mHelper doesn't = null ");
mHelper.dispose();
mHelper = null;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
Can you spot something wrong? Something i forgot?
Also this tutorial https://developer.android.com/google/play/billing/billing_integrate.html
looks much simpler, but i don't understand how to implement it, is there a sample or something from which i can see how it's implemented? I just need a simple upgrade to premium purchase
It's very hard to make it work since i can't test it personally and everytime i test it i lose money :(
Took a long time to figure out on my project but you have to understand that your mPurchaseFinishedListener is called on a non rendering thread and before your application onResume() is called (just check the IabHelper code.
So if you try to do any rendering there it CAN crash because the rendering context is not restored yet.
In your case it is likely that:
Toast.makeText(getBaseContext(), "Successo: adesso sei premium", duration).show();
will crash, the same goes for
Button test = (Button) findViewById(R.id.test);
if you check the trivia example the textButton visibility is set to true, but the button itself is a class property that is assigned in the onCreate() method.
Let me know if this helps..
Per TrivialDrive You also need (this will fix it):
// We're being destroyed. It's important to dispose of the helper here!
#Override
public void onDestroy() {
super.onDestroy();
// very important:
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
I set up the "Dungeons" InAppBilling example locally and I am ready to try it out, but I am a bit confused. I have a button like this:
Button donate = (Button)findViewById(R.id.donate);
donate.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// But what do I do here? :)
}
});
And when it is called, what do I need to do to actually go to the pay screen on android store?
Thanks!
I better suggest you to use this code as this example is quiet simple and easy to handle at first .. the things you have to do is
http://blog.blundell-apps.com/simple-inapp-billing-payment/
download the sample project code from the above link (Having Description of code and download link at bottom)
in your android project where you want to implement in app billing, create package com.android.vending.billing and place IMarketBillingService.aidl (you can find this and all files mention below in the project you downloaded in step 1)
place following utility files in any package and correct import statements accordingly.
* BillingHelper.java
* BillingReceiver.java
* BillingSecurity.java
* BillingService.java
* C.java
place the public key (you can find it in developer console in the bottom section of edit profile) in the BillingSecurity.java in line saying String base64EncodedPublicKey = "your public key here"
Declare the following permission (outside the application tag), service and receiver (Inside the application tag) in your manifest like shown below(can also see manifest which is along the code for reference)
//outside the application tag
<uses-permission android:name="com.android.vending.BILLING" />
// Inside the application tag
<service android:name=".BillingService" />
<receiver android:name=".BillingReceiver">
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
</intent-filter>
</receiver>
place the following code as mentioned with there places in your activity where purchase is being held.
//at the starting of your onCreate()
startService(new Intent(mContext, BillingService.class));
BillingHelper.setCompletedHandler(mTransactionHandler);
//outside onCreate() Within class
public Handler mTransactionHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
Log.i(TAG, "Transaction complete");
Log.i(TAG, "Transaction status: "+BillingHelper.latestPurchase.purchaseState);
Log.i(TAG, "Item purchased is: "+BillingHelper.latestPurchase.productId);
if(BillingHelper.latestPurchase.isPurchased()){
//code here which is to be performed after successful purchase
}
};
};
//code to initiate a purchase... can be placed in onClickListener etc
if(BillingHelper.isBillingSupported()){
BillingHelper.requestPurchase(mContext, "android.test.purchased");
// where android.test.purchased is test id for fake purchase, when you create products through developer console you can set a code to pass the id(which is given on developer console while creating a product) of the item which is selected for purchase to intiate purchase of that item.
} else {
Log.i(TAG,"Can't purchase on this device");
// Do Anything Heer to show user that purchase not possible on this device
}
Note: To do a test purchase you need to put the public key in BillingSecurity.java as mentioned above secondly you need to upload the apk to the developer console(you can leave it uupublished and unactive) and thirdly you need a real android device(emulator wouldn't work) having updated play store app.
Note: the account needed for in app purchase and described in all above discussion is not just simple publisher account its publisher account embedded with google merchant wallet account. The details can be found in link below.
http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113468
1)download
http://developer.android.com/guide/google/play/billing/billing_integrate.html#billing-download
2)add
http://developer.android.com/guide/google/play/billing/billing_integrate.html#billing-add-aidl
3)add permission in your android manifest file
http://developer.android.com/guide/google/play/billing/billing_integrate.html#billing-permission
now your project should look like this...
4) place the public key (you can find it in developer console in the
bottom section of edit profile) in the Security.java in line saying
String base64EncodedPublicKey = "your public key here"
5) and finally your activity which have button should be look like this
public class YourActivity extends Activity implements OnClickListener {
String issueProductId = "Your Product ID";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.updates);
SetInAppBilling();
Button donate = (Button) findViewById(R.id.donate);
donate.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
if (mBillingService.requestPurchase(issueProductId, null)) {
} else {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
Log.i("tag", "Can't purchase on this device");
}
}
});
}
public void register() {
ResponseHandler.register(mDungeonsPurchaseObserver);
}
public void unregister() {
ResponseHandler.unregister(mDungeonsPurchaseObserver);
}
public void close_unbind() {
if (mPurchaseDatabase != null)
// mPurchaseDatabase.close();
if (mBillingService != null)
mBillingService.unbind();
// stopService(new Intent(this, BillingService.class));
}
/**
* Called when this activity becomes visible.
*/
#Override
protected void onStart() {
super.onStart();
register();
}
/**
* Called when this activity is no longer visible.
*/
#Override
protected void onStop() {
unregister();
super.onStop();
}
#Override
protected void onDestroy() {
close_unbind();
super.onDestroy();
}
private static final String TAG = "YourActivity";
private static final String DB_INITIALIZED = "db_initialized";
// private static final String Dir_Check = "Dir_Check";
private DungeonsPurchaseObserver mDungeonsPurchaseObserver;
private Handler mHandler;
private BillingService mBillingService;
private PurchaseDatabase mPurchaseDatabase;
private static final int DIALOG_CANNOT_CONNECT_ID = 1;
private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2;
private Cursor mOwnedItemsCursor;
public void SetInAppBilling() {
mHandler = new Handler();
mDungeonsPurchaseObserver = new DungeonsPurchaseObserver(mHandler);
mBillingService = new BillingService();
mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase(this);
mOwnedItemsCursor = mPurchaseDatabase
.queryAllPurchasedHistroyTabelItems();
startManagingCursor(mOwnedItemsCursor);
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
boolean initialized = prefs.getBoolean(DB_INITIALIZED, false);
// Check if billing is supported.
ResponseHandler.register(mDungeonsPurchaseObserver);
if (!mBillingService.checkBillingSupported()) {
showDialog(DIALOG_CANNOT_CONNECT_ID);
}
}
private class DungeonsPurchaseObserver extends PurchaseObserver {
public DungeonsPurchaseObserver(Handler handler) {
super(YourActiviy.this, handler);
}
#Override
public void onBillingSupported(boolean supported) {
Log.i(TAG, "supportedCheck: " + supported);
if (Consts.DEBUG) {
Log.i(TAG, "supported: " + supported);
}
if (supported) {
restoreDatabase();
} else {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
}
}
#Override
public void onPurchaseStateChange(PurchaseState purchaseState,
String itemId, int quantity, long purchaseTime,
String developerPayload) {
if (Consts.DEBUG) {
Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " "
+ purchaseState);
}
if (developerPayload == null) {
} else {
}
Log.e(TAG, "onPurchaseStateChangeCheck: " + "onPurchaseStateChange");
if (purchaseState == PurchaseState.PURCHASED) {
/** TODO: */
Toast.makeText(
mContext,
"You successfully upgraded to the entire Volume One. Enjoy!",
Toast.LENGTH_SHORT).show();
finish();
}
}
#Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
if (Consts.DEBUG) {
Log.d(TAG, request.mProductId + ": " + responseCode);
}
if (responseCode == ResponseCode.RESULT_OK) {
if (Consts.DEBUG) {
Log.i(TAG, "purchase was successfully sent to server");
}
} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
if (Consts.DEBUG) {
Log.i(TAG, "user canceled purchase");
}
} else {
if (Consts.DEBUG) {
Log.i(TAG, "purchase failed");
}
}
}
#Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode) {
if (responseCode == ResponseCode.RESULT_OK) {
if (Consts.DEBUG) {
Log.d(TAG, "completed RestoreTransactions request");
}
// Update the shared preferences so that we don't perform
// a RestoreTransactions again.
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED, true);
edit.commit();
mOwnedItemsCursor = mPurchaseDatabase
.queryAllPurchasedHistroyTabelItems();
Log.d(TAG, String.valueOf(mOwnedItemsCursor.getCount()));
startManagingCursor(mOwnedItemsCursor);
if (mOwnedItemsCursor.getCount() > 0) {
Log.d(TAG, "Updating the DB");
Toast.makeText(
mContext,
"You successfully upgraded to the entire Volume One. Enjoy!",
Toast.LENGTH_SHORT).show();
finish();
}
} else {
if (Consts.DEBUG) {
Log.d(TAG, "RestoreTransactions error: " + responseCode);
}
}
}
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CANNOT_CONNECT_ID:
return createDialog(R.string.cannot_connect_title,
R.string.cannot_connect_message);
case DIALOG_BILLING_NOT_SUPPORTED_ID:
return createDialog(R.string.billing_not_supported_title,
R.string.billing_not_supported_message);
default:
return null;
}
}
private Dialog createDialog(int titleId, int messageId) {
String helpUrl = replaceLanguageAndRegion(getString(R.string.help_url));
if (Consts.DEBUG) {
Log.i(TAG, helpUrl);
}
final Uri helpUri = Uri.parse(helpUrl);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(titleId)
.setIcon(android.R.drawable.stat_sys_warning)
.setMessage(messageId)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(R.string.learn_more,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
Intent intent = new Intent(Intent.ACTION_VIEW,
helpUri);
startActivity(intent);
}
});
return builder.create();
}
/**
* Replaces the language and/or country of the device into the given string.
* The pattern "%lang%" will be replaced by the device's language code and
* the pattern "%region%" will be replaced with the device's country code.
*
* #param str
* the string to replace the language/country within
* #return a string containing the local language and region codes
*/
private String replaceLanguageAndRegion(String str) {
// Substitute language and or region if present in string
if (str.contains("%lang%") || str.contains("%region%")) {
Locale locale = Locale.getDefault();
str = str.replace("%lang%", locale.getLanguage().toLowerCase());
str = str.replace("%region%", locale.getCountry().toLowerCase());
}
return str;
}
private void restoreDatabase() {
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
boolean initialized = prefs.getBoolean(DB_INITIALIZED, false);
if (!initialized) {
mBillingService.restoreTransactions();
// Toast.makeText(this, "restoring...", Toast.LENGTH_LONG).show();
}
}
}
There are 2 ways
Maintain a database on your server end, and create a table for user+purchased list of products+expiry date
Or the client app to save a encrypted code on shared preferences (so that it cant be hacked easily)
Android API's dont provide you with any inventory apis to maintain your purchases.
I used the 2nd option.
-- single product purchase
store.purchase( { "android.test.purchased" } )
-- multi-item purchase
store.purchase( { "android.test.purchased", "android.test.canceled" } )
in reference to Getting Started with Android In-app Billing
this code maybe the one you are looking for
The sample in-app billing code from Google is a good start. I have posted a link to a tutorial I gave which is three parts. This link is the first of three. I hope it helps.
http://www.mobileoped.com/2012/04/06/android-google-play-in-app-billing-part-1/