Hello guys and good saturday everyone,
For the first time i'm implementing an in-app billing service in my app, the aim is to make a "Buy pro and remove ads" button inside app menu, quite simple actually.
Following some guides i made a BillingManager.java:
public class BillingManager implements PurchasesUpdatedListener {
private static final String TAG = "BillingManager";
private final BillingClient mBillingClient;
private final Activity mActivity;
public BillingManager(Activity activity) {
mActivity = activity;
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
#Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
#Override
public void onPurchasesUpdated(int responseCode, List<Purchase> purchases) {
Log.i(TAG, "onPurchasesUpdated() response: " + responseCode);
}
public void startPurchaseFlow(String skuId, String billingType) {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setType(billingType).setSku(skuId).build();
mBillingClient.launchBillingFlow(mActivity, billingFlowParams);
Log.i(TAG, "Received command");
}}
and then i'm calling it inside MainActivity in the menu:
if (id == R.id.action_pro) {
BillingManager mbilling = new BillingManager(MainActivity.this);
mbilling.startPurchaseFlow("mysku", BillingClient.SkuType.INAPP);
return true;
}
But when i press the button corresponding to "action_pro" Nothing happens.
All the logs shows everything is fine, onBillingSetupFinished() response is always 0 and onPurchaseUpdated() response is alwasy -1.
There aren't any crashes or error, just nothing happens...
What can i do?
Any answer is as always highly appreciated!
Edit: i put a logger for debug and i se that if put int response = mBillingClient.launchBillingFlow(mActivity, builder.build());
Log.i(TAG, "Response code: "+ response);
i get the error: I/BillingManager: Response code: -1
So actually mBillingClient.launchBillingFlow(mActivity, builder.build()); throws -1 as result... Any ideas?
Related
Hi I have this App where it displays movies from TMDB I am having an issue where I can't display a feedback to users that may lead them to keep unnecessarily waiting When my app starts without internet or server returns no data
public MainViewModel(#NonNull Application application) {
super(application);
AppDatabase appDatabase = AppDatabase.getInstance(this.getApplication());
favoriteMovies = appDatabase.favoriteDao().loadAllFavorites();
Call<ApiResults> call = Network.buildAPICall(Network.POPULAR);
call.enqueue(new Callback<ApiResults>() {
#Override
public void onResponse(Call<ApiResults> call, Response<ApiResults> response) {
if (response.message().contentEquals("OK")) {
popularMovies.setValue(response.body().getMovies());
} else {
Log.e(TAG, "Something unexpected happened to our request: " + response.message());
}
}
#Override
public void onFailure(Call<ApiResults> call, Throwable t) {
Log.i(TAG, "Something unexpected happened to our request: " );
Log.e(TAG, t.getMessage());
}
});
I want to display the message "Something unexpected happened to our request: " when there is no internet access to the mainActivity the problem is I can't display a toaster in the view model class
Here is my main Activity code snippet
public void setupViewModel() {
com.example.popularmovies.UI.MainViewModel viewModel = ViewModelProviders.of(this).get(com.example.popularmovies.UI.MainViewModel.class);
Log.i("Test",""+ viewModel);
viewModel.getFavoriteMovies().observe(this, new Observer<List<MovieData>>() {
#Override
public void onChanged(#Nullable List<com.example.popularmovies.Data.MovieData> favoriteEntries) {
Log.d(TAG, "Receiving changes from LiveData");
if (mSortOrder.contentEquals(FAVORITE)) {
List<com.example.popularmovies.Data.MovieData> movieList = new ArrayList<com.example.popularmovies.Data.MovieData>();
if (favoriteEntries != null) {
for (com.example.popularmovies.Data.MovieData fave : favoriteEntries) {
fave.setFavorite(1);
}
setAdapter(favoriteEntries);
}
}
}
});
viewModel.getTopRatedMovies().observe(this, new Observer<List<com.example.popularmovies.Data.MovieData>>() {
#Override
public void onChanged(#Nullable List<com.example.popularmovies.Data.MovieData> movieData) {
Log.i("Test",""+ movieData);
if (movieData != null && mSortOrder.contentEquals(com.example.popularmovies.Utils.Network.TOP_RATED)) {
setAdapter(movieData);
}
}
});
viewModel.getPopularMovies().observe(this, new Observer<List<com.example.popularmovies.Data.MovieData>>() {
#Override
public void onChanged(#Nullable List<com.example.popularmovies.Data.MovieData> movieData) {
Log.i("Test",""+ movieData);
if (movieData != null && mSortOrder.contentEquals(com.example.popularmovies.Utils.Network.POPULAR)) {
setAdapter(movieData);
}
}
});
}
Any suggestions how to do that?
Use LiveData, An Observable data holder class, also and Lifecycle aware in your case Activity Lifecycle.
Declare Variable
private MutableLiveData<String> toastMessageObserver = new MutableLiveData();
Set Value
toastMessageObserver.setValue("Something unexpected happened to our request: "+response.message()); // Whenever you want to show toast use setValue.
Getter Method
Define getter method in viewModel
public LiveData<String> getToastObserver(){
return toastMessageObserver;
}
In activity inside setupViewModel
viewModel.getToastObserver().observe(this, message -> {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
});
I just completed setting up inapp billing using google play billing with aidl.
On successful purchase the premium feature is activated through a boolean. But after the app is closed and relaunched, premium feature disappears. i.e the boolean reverts to false. I would like to know how to ensure the boolean stays as true after app launch as long as premium has been purchased.
On MainActivity
public class MainActivity extends AppCompatActivity {
public static boolean proFeature = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
On InAppBilling Activity
public class InAppBilling extends Activity implements IabBroadcastReceiver.IabBroadcastListener {
private static final String TAG = ".InAppBilling";
IabHelper mHelper;
boolean premiumFeature = false;
static final String SKU_PREMIUM = "android.test.purchased";
static final int RC_REQUEST = 10001;
IabBroadcastReceiver mBroadcastReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_lay);
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
Log.d(TAG, "Query inventory finished.");
if (mHelper == null) return;
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inv.getPurchase(SKU_PREMIUM);
premiumFeature = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (premiumFeature ? "PREMIUM" : "NOT PREMIUM"));
updateUi();
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
#Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
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!");
premiumFeature = true;
updateUi();
setWaitScreen(false);
}
}
};
public void updateUi(){
button.setVisibility(premiumFeature ? View.GONE : View.VISIBLE);
if (premiumFeature){
MainActivity.proFeature = true;
}else{
MainActivity.proFeature = false;
}
}
In your purchase finish listener, modify below code to store value into shared preferance.
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!");
premiumFeature = true;
SharedPreferences sharedPref = context.getSharedPreferences(
"my_sp", Context.MODE_PRIVATE);
sharedPref.edit().putBoolean("isPremium, premiumFeature).commit();
updateUi();
setWaitScreen(false);
}
And on app relaunch get this value from shared preference again.
SharedPreferences sharedPref = context.getSharedPreferences(
"my_sp", Context.MODE_PRIVATE);
premiumFeature = sharedPref.getBoolean("isPremium, false);
Update(19 Feb, 2022):
As #Shazniq said, It's always good to store those details on the server for safety, along with user profile data. So on every launch of the app, you can verify the details. You have to apply your own logic to verify it whenever you need it.
You can save it to sharedpreferences or on your server and query the status in beginning(splash screen).
or you can do a inventory query in splash screen to check the status in the splash screen.
trying to use in app billing in Android at the moment.
Everything works fine and I can purchase items and query existing ones but my OnIabPurchaseFinishedListener listener is not being called during a successful purchase. It is called when there is an issue but not on a completed purchase.
I have my main activity, with fragments and a navigation drawer. (fragments are not touched here. I have a helper class that all the billing happens in.
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, MyBilling.Update {\
MyBilling bill;
OnCreate(){
bill = new MyBilling(this, this);
bill.onCreate();
}
private void RemoveAdsClick()
{
bill.purchaseRemoveAds();
}
public void NewPurchaseUpdate(){
tinydb.putBoolean("Premium", true);
nav_Menu.findItem(R.id.remove_ad_button).setVisible(false);
displaySelectedScreen(R.id.distance_check); // Reload screen
}
}
public class MyBilling extends Activity {
////Interface
public interface Update {
void NewPurchaseUpdate();
}
// Debug tag, for logging
static final String TAG = "XXXXXX";
static final String SKU_REMOVE_ADS = "remove_ads";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10111;
// Activity
Activity activity;
// The helper object
IabHelper mHelper;
String base64EncodedPublicKey = "xxxxxxxx";
String payload = "xxxxx";
public boolean isAdsDisabled = false;
public boolean AdCheck = false;
////Instance of interface
Update myActivity;
public MyBilling(Activity launcher,Update activity) {
this.activity = launcher;
myActivity = activity;
}
// User clicked the "Remove Ads" button.
public void purchaseRemoveAds() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(activity, SKU_REMOVE_ADS,
RC_REQUEST, mPurchaseFinishedListener, payload);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// 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.");
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: "
+ purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null)
return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_REMOVE_ADS)) {
// bought the premium upgrade!
myActivity.NewPurchaseUpdate();
Log.d(TAG, "New Purchase Update Method was called");
}
}
};
}
some googling showed that it could be an issue with onActivityResult not being in the helper class. So I made billing class extend activity and then added onActivityResult and #Override but this also is not called with any breakpoints.
As mentioned above, OnIabPurchaseFinishedListener is called for a failed purchase (already own item) but not for a successful one.
Any help here would be great, I have tried to keep the code as light as possible to help with reading. If I'm missing anything please let me know.
I have fixed this issue by ditching my helper class for now and moving the billing tasks into the main activity.
Hi I'm try to get Google Play Service Ads Id in AsyncTask.
I read Android AsyncTask API documentation and many stackoverflow Answer. I want to get Ads Id first and will start other process.
Accordingly, I made AdIdAsyncTask for getting Ads Id in BackgroundThread and invoke in my MainActivity. But My AsyncTask status is always RUNNING.
What's Wrong?
MainActivity and AdIdAsyncTask
public class MainActivity extends Activity {
private static final String TAG = "DummyActivity";
private final long MEDIA_ID = 292929L;
private final String ACCESS_KEY = "xxxx-xxxx-xxxx";
private String adId = null;
private AdContext adContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AdIdAsyncTask task = new AdIdAsyncTask();
task.execute();
Log.d(TAG, "task.getStatus=" + task.getStatus());
if (task.getStatus().equals(AsyncTask.Status.FINISHED)) {
// do work
}
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
}
public class AdIdAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
Log.d(TAG, "doInBackground");
AdvertisingIdClient.Info idInfo = null;
try {
idInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext());
} catch (GooglePlayServicesNotAvailableException | GooglePlayServicesRepairableException | IOException e) {
Log.e(TAG, "fetch Google Ads is failed. message=" + e.getMessage());
}
String advertisingId = null;
try {
advertisingId = idInfo.getId();
} catch (NullPointerException e) {
Log.e(TAG, "adId is null. message=" + e.getMessage());
}
adId = advertisingId;
cancel(true);
Log.i(TAG, "adId=" + advertisingId + ", isCancelled=" + isCancelled());
return null;
}
#Override
protected void onCancelled() {
super.onCancelled();
Log.i(TAG, "onCancelled");
}
#Override
protected void onCancelled(Void aVoid) {
super.onCancelled(aVoid);
Log.i(TAG, "onCancelled");
}
}
}
Log
D/DummyActivity: task.getStatus=RUNNING
D/DummyActivity: doInBackground
I/DummyActivity: adId=xxxx-xxxx-xxxx, isCancelled=true
I/DummyActivity: onCancelled
I/DummyActivity: onCancelled
SdkVersion
compileSdkVersion 25
minSdkVersion 14
targetSdkVersion 25
Hello you can force close the AsyncTask
Please use the following code in yout AsyncTask with condition and call it in your activity where you want to stop, it will stop your asynk task running
/**
* Check if asynctask is running, if still running cancel it.
*/
public void forceCancel(){
if (getStatus().equals(AsyncTask.Status.RUNNING)) {
cancel(true);
}
}
I downloaded the sample app
Got my key from the developer console
Put the proper permissions in my manifest
Nothing helps: I got always product not found...
Thanx - Ralf
here is the code, Java, Android Studio
// Does the user have the professional upgrade? should be an In-App product
private static Boolean _PROFESSIONAL = false;
// Debug tag, for logging
private static final String TAG_BILLING = "BillingService";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001; // 10002 does not help either...
// The helper object
IabHelper mHelper = null;
static final String SKU_PROFESSIONAL = "com...";
private String base64EncodedPublicKey = "KSRWbpF........... from Google play Developer Console
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// Create the helper, passing it our context and the public key to verify signatures with
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// IAB is fully set up. Now, let's get an inventory of stuff we own.
mHelper.queryInventoryAsync(true, null, mGotInventoryListener);
}
});
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG_BILLING, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PROFESSIONAL);
_PROFESSIONAL = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
}
};
// User clicked the menu upgrade professional
private void Upgrade() {
String payload = sMyPurchaseToken;
// tested, but no help
// define IabHelper.flagEndAsync() as public, and add iabHelper.flagEndAsync() before iabHelper.launchPurchaseFlow(...)
// mHelper.flagEndAsync();
// could be tested: this, "android.test.purchased", 10002
/////////// here the program shows the Google Play message that the programm does not exist... or is not configured for payment...
mHelper.launchPurchaseFlow(MyActivity.this, SKU_PROFESSIONAL, RC_REQUEST, mPurchaseFinishedListener, payload);
//mHelper.launchPurchaseFlow(this, "android.test.purchased", 10002, is ok
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG_BILLING, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
}
else {
}
}
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
return true;
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
return;
}
if (purchase.getSku().equals(SKU_PROFESSIONAL)) {
_PROFESSIONAL = true;
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG_BILLING, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isSuccess()) {
}
else {
complain("Error while consuming: " + result);
}
}
};