I am trying to detect if any of the in-app purchases are owned by the user when the app is started on first try to renew the Pro mode of the app using SharedPreferences. The following code is unfortunately not working :(
if (version.equals("null")) { //checking version of the app, if it is unset equals first launch
SharedPreferences.Editor editor = appinfo.edit();
version = currentversion;
editor.putString("version", version);
editor.apply();
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (mHelper == null) return; //IabHelper mHelper;
Purchase purchase = Inventory.getPurchase("sku1");
Purchase purchase2 = Inventory.getPurchase("sku2");
Purchase purchase3 = Inventory.getPurchase("sku3");
if (purchase != null || purchase2 != null || purchase3 != null) {
final SharedPreferences ispro = getApplicationContext().getSharedPreferences("ispro", 0);
SharedPreferences.Editor editor = ispro.edit();
editor.putInt("ispro", 1);
editor.apply();
}
}
};
startActivity(new Intent(MainPage.this, Changelog.class));
EDIT1: After some edits the code now looks like this:
final List<String> skus = Arrays.asList("sku1", "sku2", "sku3");
if (version.equals("null")) {
SharedPreferences.Editor editor = appinfo.edit();
version = currentversion;
editor.putString("version", version);
editor.apply();
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
}
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(MainPage.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (mHelper == null) return;
Purchase purchase = Inventory.getPurchase("sku1");
Purchase purchase2 = Inventory.getPurchase("sku2");
Purchase purchase3 = Inventory.getPurchase("sku3");
if (purchase != null || purchase2 != null || purchase3 != null) {
final SharedPreferences ispro = getApplicationContext().getSharedPreferences("ispro", 0);
SharedPreferences.Editor editor = ispro.edit();
editor.putInt("ispro", 1);
editor.apply();
}
}
};
try {
mHelper.queryInventoryAsync(true, skus, null, mReceivedInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
}
}
});
startActivity(new Intent(MainPage.this, Changelog.class));
I am not aware what is wrong with this code. Thank you in advance for the help and Happy New Year! :)
You must call IabHelper.queryInventoryAsync() in order for a QueryInventoryFinishedListener to do anything useful. Just add a call to that function immediately prior to your startActivity() call. (This is assuming you've already called IabHelper.startSetup() and all that good stuff first.)
You cannot refer to a local variable prior to its declaration. The reason you got a "mReceivedInventoryListener cannot be resolved" error is because the answer referenced in your example swapped the two lines in a confusing way.
Obligatory mention: IabHelper is apparently no longer supported by Google; you're supposed to use the billing client library instead.
Related
The problem is not to get the Dialog to show up at all. It shows up when I comment out stuff behind dialog.show(). If i do not comment the lines behind it out, it will simple wait after the uploading in the line: 'boolean uploadSuccessful =', which just means it will never show up because i dismiss it there. It still freezes tho. Sorry for the bad explanation. And yes, I am quite new to coding for/in Android... And also yes there is a lot of code which should not change the behavior of the dialog, just didnt want to have to less to be seen. Thanks for every help in advanced! Also nothing here should be async. Please take a look:
`
public static void uploadData(Context context, int pid, String patientName, Resources resources, AlertDialog.Builder builder, ChooseWoundActivity chooseWoundActivity) {
//For showing the progressbar
LayoutInflater inflater = chooseWoundActivity.getLayoutInflater();
builder = new AlertDialog.Builder(chooseWoundActivity);
builder.setView(inflater.inflate(R.layout.loading_dialog, null));
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
//When commenting out everything from here on the dialog is shown if not it will be stuck with the old dialog before that.
DatabaseHelper db = new DatabaseHelper(context);
ArrayList<Wound> wounds = db.getAllWoundsOfPatient(pid);
ArrayList<UploadData> uploadDataArrayList = new ArrayList<>();
if (wounds == null || wounds.size() == 0) {
Toast.makeText(context, resources.getString(R.string.noWounds) + " " + patientName + " " + resources.getString(R.string.found), Toast.LENGTH_LONG).show();
}
for (Wound w : wounds) {
if (w != null) {
UploadData data = db.getLastData(pid, w.getId());
if (data != null) {
db.addLastUpload(pid, w.getId(), data.getDate());
uploadDataArrayList.add(data);
}
}
}
for (UploadData data : uploadDataArrayList) {
if (data != null) {
db.addToUploadQueue(data.getPatientId(), data.getWoundId());
}
}
ArrayList<UploadData> uploadDataArrayListQueue = db.getDataOfQueue();
for (int i = 0; i < uploadDataArrayListQueue.size(); i++) {
UploadData data = uploadDataArrayListQueue.get(i);
ArrayList<Task> tasksToDelete = new ArrayList<>();
try {
for (Task task : data.getTasks()) {
if (task == null || task.getValue() == null) {
tasksToDelete.add(task);
}
}
} catch (NullPointerException ex) {
Log.e("NullpointerException", "Created by data.getTasks()");
}
for (Task task : tasksToDelete) {
data.getTasks().remove(task);
}
ArrayList<Perspective> perspectivesToDelete = new ArrayList<>();
for (Perspective perspective : data.getPerspectives()) {
if (perspective == null || (perspective.getBitmapPre() == null && perspective.getBitmapPost() == null)) {
perspectivesToDelete.add(perspective);
//Log.d("FPD", "Adding more perspectives to the upload.");
}
}
//Log.d("FPD", "There the whole data: " + data + ". Here the perspectives: " + data.getPerspectives());
for (Perspective perspective : perspectivesToDelete) {
//Log.d("FPD", "Removing a perspective");
data.getPerspectives().remove(perspective);
}
}
// Uncomment this to enable uploading of wounds, pass the URL of your local web service as the second argument in the UploadService constructor
boolean uploadSuccessful = ConnManager.getInstance(context, null, "foo", "bar", true, true).upload(uploadDataArrayListQueue);
dialog.dismiss();
if (!uploadSuccessful) {
//TODO: Add it into the queue for next upload
/*for (UploadData data : uploadDataArrayList) {
if (data != null) {
db.addToUploadQueue(data.getPatientId(), data.getWoundId());
}
}*/
builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.attention)
.setMessage(resources.getString(R.string.noInternetUploadLater))
.setPositiveButton("OK", null);
} else {
db.deleteQueue();
builder = new AlertDialog.Builder(context);
builder.setTitle("")
.setMessage(resources.getString(R.string.uploadSuccessful))
.setPositiveButton("OK", null);
}
builder.create();
builder.show();
chooseWoundActivity.onResume();
}
`
I am trying to open zoom app by passing uri to the intent with below and it works fine.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("<zoom url>"))
val packageManager = packageManager
if (intent.resolveActivity(packageManager) != null) {
startActivity(launchApp)
}
But this shows my browser also as user can select it and open the uri I pass. what I want to do is only open zoom app with the uri.
By using val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) I can filter the apps that can open my intent but how can I select the exact app(zoom) and pass uri to it and open zoom app only with my meeting url?
May be need a packageName?
I referenced some code from Google in this
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(activity, uri);
}
}
it use setPackage(),then the app will open the chrome app without chooser.
This is the method of getPackageNameToUse
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
...
...
public static String getPackageNameToUse(Context context) {
if (sPackageNameToUse != null) return sPackageNameToUse;
PackageManager pm = context.getPackageManager();
// Get default VIEW intent handler.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
// Get all apps that can handle VIEW intents.
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
sPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
sPackageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
sPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
sPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
sPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
sPackageNameToUse = LOCAL_PACKAGE;
}
return sPackageNameToUse;
}
Maybe it will help you.
I have implemented In-App Billing in my Activity
this is my onIabPurchaseFinished() method:
#Override
public void onIabPurchaseFinished(IabResult result, Purchase info) {
if (!verifyDeveloperPayload(info)) {
Toast.makeText(this, R.string.error_purchasing, Toast.LENGTH_LONG).show();
}
Toast.makeText(this, R.string.premium_bought, Toast.LENGTH_LONG).show();
if (info.getSku().equals("chords_premium")) {
/** salva isPremium tra SharedPreferences */
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("status", "purchased");
editor.apply();
}
}
As you can see I save the String "status" to SharedPreferences so that I can access it from anywhere, and keep it stored even after the app is closed.
Then in my other Activities where ads are implemented I wrote like this:
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final String status = prefs.getString("status", "free");
/** gestisce le pubblicita */
if (status.equals("free")) {
MobileAds.initialize(getApplicationContext(), "ca-app-pub-6723047396589178/2654753246");
AdView listBanner = (AdView) findViewById(R.id.chords_list_banner);
AdRequest adRequest = new AdRequest.Builder().build();
listBanner.loadAd(adRequest);
/** carica Ad a tutto schermo */
chordsListAd = new InterstitialAd(this);
chordsListAd.setAdUnitId("ca-app-pub-6723047396589178/7447672046");
requestNewInterstitial();
chordsListAd.setAdListener(new AdListener() {
#Override
public void onAdClosed() {
requestNewInterstitial();
}
});
}
As you can see here the Ads are surrounded by an if statement that checks if the "status"String is set to free.
The problem is that when I buy Premium, the Ads are still shown. How can I fix it?
It's because you are saving your data in Base Context and trying to find it in the current Activity context using (this).
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
to
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
Also, a more recommeded way to query In-App purchased items is to query In- App inventory instead of storing in sharedprefs.
As mention in Google Docs
Query Purchased Items
Upon a successful purchase, the user’s purchase data is cached locally by Google Play’s In-app Billing service. It is good practice to frequently query the In-app Billing service for the user’s purchases, for example whenever the app starts up or resumes, so that the user’s current in-app product ownership information is always reflected in your app.
To retrieve the user’s purchases from your app, call queryInventoryAsync(QueryInventoryFinishedListener) on your IabHelper instance. The QueryInventoryFinishedListener argument specifies a listener that is notified when the query operation has completed and handles the query response. It is safe to make this call fom your main thread.
mHelper.queryInventoryAsync(mGotInventoryListener); //mHelper is IabHelper instance
If the query is successful, the query results are stored in an Inventory object that is passed back to the listener. The In-app Billing service returns only the purchases made by the user account that is currently logged in to the device.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
// update UI accordingly
}
}
};
Check if the inapp purchase is made:
//*************************************checking in app purchase has been made********************************//
void testInApp()
{
if (!blnBind) return;
if (mService == null) return;
int result;
try {
result = mService.isBillingSupported(3, getPackageName(), "inapp");
//Toast.makeText(context, "isBillingSupported() - success : return " + String.valueOf(result), Toast.LENGTH_SHORT).show();
Log.i(tag, "isBillingSupported() - success : return " + String.valueOf(result));
} catch (RemoteException e) {
e.printStackTrace();
//Toast.makeText(context, "isBillingSupported() - fail!", Toast.LENGTH_SHORT).show();
Log.w(tag, "isBillingSupported() - fail!");
return;
}
}
void checkInApp()
{
if (!blnBind) return;
if (mService == null) return;
Bundle ownedItems;
try {
ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
//Toast.makeText(context, "getPurchases() - success return Bundle", Toast.LENGTH_SHORT).show();
Log.i(tag, "getPurchases() - success return Bundle");
} catch (RemoteException e) {
e.printStackTrace();
//Toast.makeText(context, "getPurchases - fail!", Toast.LENGTH_SHORT).show();
Log.w(tag, "getPurchases() - fail!");
return;
}
int response = ownedItems.getInt("RESPONSE_CODE");
//Toast.makeText(context, "getPurchases() - \"RESPONSE_CODE\" return " + String.valueOf(response), Toast.LENGTH_SHORT).show();
Log.i(tag, "getPurchases() - \"RESPONSE_CODE\" return " + String.valueOf(response));
if (response != 0) return;
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
Log.i(tag, "getPurchases() - \"INAPP_PURCHASE_ITEM_LIST\" return " + ownedSkus.toString());
Log.i(tag, "getPurchases() - \"INAPP_PURCHASE_DATA_LIST\" return " + purchaseDataList.toString());
Log.i(tag, "getPurchases() - \"INAPP_DATA_SIGNATURE\" return " + (signatureList != null ? signatureList.toString() : "null"));
Log.i(tag, "getPurchases() - \"INAPP_CONTINUATION_TOKEN\" return " + (continuationToken != null ? continuationToken : "null"));
// TODO: management owned purchase
try {
if(purchaseDataList.size()>0){
jinapp=new JSONArray(purchaseDataList.toString());
JSONObject c = jinapp.getJSONObject(0);
String productid=c.getString("productId");
if(productid!=null){
SharedPreferences.Editor editor = prefpurchase.edit();
editor.putBoolean(Constants.APP_IS_PURCHASED,true);
editor.commit();
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO: management owned purchase
}
Write the code in your SplashScreen
Now in the activity/fragment in which you are showing your ad,write the following code:
//*******************to check purchase has been made.If yes disable ads and no then show ads******************//
prefpurchase = this.getSharedPreferences(Constants.GET_IN_APP_STATE, Context.MODE_PRIVATE);
//Toast.makeText(context, "bindService - return " + String.valueOf(blnBind), Toast.LENGTH_SHORT).show();
//In App Purchase
ispurchased=prefpurchase.getBoolean(Constants.APP_IS_PURCHASED,false);
System.out.println("ispurchased-->"+ispurchased);
if(ispurchased)
{
setContentView(R.layout.activity_home_noads);
}else{
System.out.println("Getting ad");
setContentView(R.layout.activity_home);
//Locate the Banner Ad in activity_main.xml
AdView adView = (AdView) this.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder()
// Add a test device to show Test Ads
//.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
//.addTestDevice("B2D63***************************")
.build();
// Load ads into Banner Ads
adView.loadAd(adRequest);
}
//*******************************************************************************************************//
The logic is simple,you are creating two versions of your layout,one with ad and the other without ad.
Load the correct layout depending on the value of sharedpreference.
mService:
Write this code globally in splashscreen before onCreate():
private IInAppBillingService mService;
private ServiceConnection mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};
blnBind
Declare blnBind globally:
boolean blnBind;
In onCreate() of SplashActivity write:
// Bind Service
blnBind = bindService(new Intent(
"com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
//Toast.makeText(context, "bindService - return " + String.valueOf(blnBind), Toast.LENGTH_SHORT).show();
Log.i(tag, "bindService - return " + String.valueOf(blnBind));
//In App Purchase
GET_IN_APP_STATE or APP_IS_PURCHASED are created for shared Preferences,that acts as a key for preference values.
//Preferences to check in app purchase
final static public String GET_IN_APP_STATE = "prefinapp";
public static final String APP_IS_PURCHASED ="AppIsPurchased";
Whenever a purchase is made,don't forget to set the shared preference value to true.
I followed these instructions to integrate both Libgdx and native android code using ActionResolver interface. I have no problem calling the Android method from the Libgdx part of my code. But I am hitting a dead end when I am trying to intergrate Google IAP with Libgdx. According to TrivialDrive example, it uses mPurchaseFinishedListener (outside of calling method).
My question is: how do I pass this IAP resultcode back to Libgdx since the listener is outside the calling method? Currently, purchase process went through, but the libgdx part of my code is not being "informed" of the purchase status/result.
This is my code:
Any help is much appreciated.
ActionResolver:
public interface IActionResolver {
public int requestIabPurchase(int product);
}
MainActivity:
public class MainActivity extends AndroidApplication implements IActionResolver {
// Debug tag, for logging
static final String TAG = "greatgame";
// Does the user have the premium upgrade?
boolean mIsUpgraded = false;
// SKUs for our products: the cat, all, or pow
static final String SKU_UPGRADE = "android.test.purchased";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;
// The helper object
IabHelper mHelper;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
initialize(new Catland(this), cfg);
}
void iAbStartup() {
String base64EncodedPublicKey = "some key";
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
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.
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up in-app billing: " + result);
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.
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "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()) {
Log.d(TAG, "Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the SKU_UPGRADE upgrade?
Purchase thisUpgrade = inventory.getPurchase(SKU_UPGRADE);
mIsUpgraded = (thisUpgrade != null && verifyDeveloperPayload(thisUpgrade));
Log.d(TAG, "User is " + (mIsUpgraded ? "Upgraded" : "Free"));
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
runPurchaseFlow(submitProduct);
}
};
// Run real purchase flow
public void runPurchaseFlow(int product) {
Log.d(TAG, "runPurchaseFlow");
/* TODO: for security, generate your payload here for verification. See the comments on
* verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
* an empty string, but on a production app you should carefully generate this. */
String payload = "";
if (product == 1)
mHelper.launchPurchaseFlow(this, SKU_UPGRADE, RC_REQUEST, mPurchaseFinishedListener, payload);
}
// 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);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
Log.d(TAG, "Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_CAT)) {
// bought the upgrade!
Log.d(TAG, "Purchase Upgrade. Congratulating user.");
mIsUpgraded = true;
// how do i pass this result to the libgdx?
}
}
};
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
return true;
}
#Override
public int requestIabPurchase(int product) {
iAbStartup();
return 0; // how do i get the result from mPurchaseFinishedListener?
}
}
PurchaseScreen
result = greatgame.actionResolver.requestIabPurchase(1);
You won't be able to return the result from requestIabPurchase() - the only methods of doing so would block for a long time. The best way, in my opinion, would be to create a listener interface of your own that your LibGdx project implements, and pass that into your request interface. For example:
In your libGdx project somewhere:
interface PurchaseCallback {
public int setPurchaseResult(int result);
}
ActionResolver:
public interface IActionResolver {
public int requestIabPurchase(int product, PurchaseCallback callback);
}
In PurchaseScreen, implement PurchaseCallback:
#override
public int setPurchaseResult(int result) {
// Yay! I have a result from a purchase! Maybe you want a boolean instead of an int? I don't know. Maybe an int (for the product code) and a boolean.
}
...and pass whatever is implementing PurchaseCallback (I'm assuming your PurchaseScreen does itself):
result = greatgame.actionResolver.requestIabPurchase(1, this);
Finally, hook it all up in MainActivity:
PurchaseCallback mCallback = null;
mPurchaseFinishedListener = ... etc. etc.
.
.
.
if (mCallback != null) {
mCallback.setPurchaseResult(0);
}
.
.
.
#Override
public int requestIabPurchase(int product, PurchaseCallback callback) {
mCallback = callback; // save this for later
iAbStartup();
return 0;
}
Note that you should call PurchaseCallback.setPurchaseResult() everywhere that mPurchaseFinishedListener has return, not only at the line // how do i pass this result to the libgdx? - otherwise, you will never know if a purchase failed or is just taking a really long time.
What would be the most logical way to go about launching an activity when an app is opened for the first time after an update. I understand that a sharedprefs would be the easiest way for this, but sharedprefs are persistent across application updates so it wouldn't seem that that option would work. Any ideas?
Make the shared pref store the version number of the app: if the version is different, update it and then launch your Activity.
EDIT: this is what I do in my what's new check. It loads up the app info, fetches the version number and if it has changed it pops open the Activity.
public static boolean show(Context c)
{
ApplicationInfo ai = c.getApplicationInfo();
String basis = ai.loadLabel(c.getPackageManager()).toString();
try {
PackageInfo pi = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_META_
DATA);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
String lastVersion = prefs.getString("lastversion", null);
SharedPreferences.Editor edit = prefs.edit();
if(lastVersion == null){
// save the first installed version so we can check and see when it got installed
edit.putString("firstversion", String.valueOf(pi.versionCode));
edit.commit();
}
if(lastVersion == null || pi.versionCode > Integer.parseInt(lastVersion)){
edit.putString("lastversion", String.valueOf(pi.versionCode));
edit.commit();
// show it
Intent i = new Intent(c, WhatsNew.class);
c.startActivity(i);
return true;
}
} catch (Exception e) {
android.util.Log.v("WhatsNew", "Exception checking for release notes for [" + basis +
"]:" + e.getMessage(), e);
}
return false;
}