I already have in my application the integration with "in-app-billing-purchase" however I have several activity where I need to be duplicating all the code.
So I thought about leaving everything focused on a single class, I wastancio the class and I arrive if the item was bought.
Just that I have a big problem the "in-app-billing-purchase" uses Listener OnIabPurchaseFinishedListener and QueryInventoryFinishedListener
How can I leave all this organized in a single class and just check with a simple call in my activity?
Calling code:
Billing bl = new Billing(getActivity().getApplicationContext());
if (bl.Comprado())
Toast.makeText(getActivity(), "Comprado", Toast.LENGTH_SHORT).show();
else
Toast.makeText(getActivity(), "Erro comprado", Toast.LENGTH_SHORT).show();
Class I created to be called multiple times
public class Billing {
// Item name for premium status
private static final String SKU_PREMIUM = "premium";
// private static final String SKU_PREMIUM = "tirarbanner";
// Flag set to true when we have premium status
private static boolean mIsPremium = false;
// In-app Billing helper
private IabHelper mAbHelper;
public Billing(Context context) {
String base64EncodedPublicKey = "xxx";
// Create in-app billing helper
mAbHelper = new IabHelper(context, base64EncodedPublicKey);
// and start setup. If setup is successfull, query inventory we already
// own
mAbHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
return;
}
mAbHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
public static boolean Comprado() {
return mIsPremium;
}
/**
* Listener that is called when we finish purchase flow.
*/
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
return;
}
// Purchase was successfull, set premium flag and update interface
if (purchase.getSku().equals(SKU_PREMIUM)) {
mIsPremium = true;
}
}
};
/**
* 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) {
// Have we been disposed of in the meantime? If so, quit.
if (mAbHelper == null)
return;
// Is it a failure?
if (result.isFailure()) {
return;
}
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
mIsPremium = premiumPurchase != null;
}
};
}
There's already a library for that.
Check android-inapp-billing-v3.
Related
I have an Android project where I want to use com.android.billingclient.api version 4.0.0, which would replace an old billing library that google doesn't allow any more (com.anjlab.android.iab.v3). I've implemented the methods for a one-time purchase, but when querying the SKU Details with billingClient.querySkuDetailsAsync using the SKU string for the product, I get an empty result set. I've been assured that the SKU is correct, so I don't know where the error might be.
Also, the old implementation required to provide a license key, which isn't the case with the new library. Do I need to define it somewhere else in the app?
Here's the code where it fails:
List<String> skuList = new ArrayList<>();
skuList.add(SKU_ID);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
final Activity v = this;
billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Has anyone a suggestion what to do?
This is how I query the SKU details within my app.
You can try to use this example and see if this works for you.
billingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
Log.d(TAG, "Connection finished");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
List<String> skuList = new ArrayList<> ();
skuList.add(ITEM_SKU_ADREMOVAL);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
(billingResult1, skuDetailsList) -> {
// Process the result.
if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
for (Object skuDetailsObject : skuDetailsList) {
skuDetails = (SkuDetails) skuDetailsObject;
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
if (ITEM_SKU_ADREMOVAL.equals(sku)) {
removeadsPrice = price;
}
else {
Log.d(TAG,"Sku is null");
}
}
Log.d(TAG, "i got response");
Log.d(TAG, String.valueOf(billingResult1.getResponseCode()));
Log.d(TAG, billingResult1.getDebugMessage());
}
else if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.ERROR) {
Toast.makeText(MainActivity.this, "Error in completing the purchase!", Toast.LENGTH_SHORT).show();
}
});
}
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_TIMEOUT) {
Toast.makeText(MainActivity.this, "Service timeout!", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(MainActivity.this, "Failed to connect to the billing client!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBillingServiceDisconnected() {
restartConnection();
}
});
Getting this one working properly depends on several different factors
Have you published your app to Play Console or at least to an
internal track or something?
Do you have active products or subscriptions on your Google Play
Console?
Have you configured your licensed testers?
Please see the documentation for more info.
Am trying to implement Braintree Vault PayPal payment, the problem am facing here is getting the paymentMethodNonce my event listener createdListener to capture nonce doesn't get called using vault, but everything works fine using checkout. I can't charge customer without a paymentMethodNonce, please can anyone assist me.
mBraintreeFragment = BraintreeFragment.newInstance(this,"TOKEN_FROM_SERVER");
PayPalRequest request = new PayPalRequest().localeCode("US").billingAgreementDescription("Your agreement description");
PayPal.requestBillingAgreement(mBraintreeFragment, request);
mBraintreeFragment.addListener(createdListener);
mBraintreeFragment.addListener(cancelListener);
mBraintreeFragment.addListener(errorListener);
DataCollector.collectDeviceData(mBraintreeFragment, new BraintreeResponseListener<String>() {
#Override
public void onResponse(String deviceData) {
Log.e("PayPal", deviceData);
try {
JSONObject json = new JSONObject(deviceData);
deviceDataInfo = json.getString("correlation_id");
Log.e("PayPal", deviceDataInfo);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
My Listeners
PaymentMethodNonceCreatedListener createdListener = new PaymentMethodNonceCreatedListener() {
#Override
public void onPaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNonce) {
String nonce = paymentMethodNonce.getNonce();
Log.d("PayPal", "nonce id " + nonce);
}
};
BraintreeCancelListener cancelListener = new BraintreeCancelListener() {
#Override
public void onCancel(int requestCode) {
Log.d("CreditCard", "Braintree Error Code " + requestCode);
}
};
BraintreeErrorListener errorListener = new BraintreeErrorListener() {
#Override
public void onError(Exception error) {
if (error instanceof ErrorWithResponse) {
ErrorWithResponse errorWithResponse = (ErrorWithResponse) error;
BraintreeError cardErrors = errorWithResponse.errorFor("creditCard");
if (cardErrors != null) {
List<BraintreeError> errors = cardErrors.getFieldErrors();
String err = Objects.requireNonNull(errors.get(0).getMessage());
Log.d("CreditCard", errors.toString());
}
}
}
};
Instead of adding manually your listeners to that request, it's better to just implement the interface from braintree.
For example, if you want to use the onPaymentMethodNonceCreated() just add "implements PaymentMethodNonceCreatedListener" after your class name.
public class "YourClass" implements PaymentMethodNonceCreatedListener {
//...
}
And then override the method that now the Android Studio is warning you:
#Override
public void onPaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNonce) {
String nonce = paymentMethodNonce.getNonce();
//...
}
This way you can go for everyone of your listeners! Good luck!
on API > 21 when my phone is locked this service is stop working (when I wake up a phone a service is starting working). This is what I do :
public class JobDispacherService extends JobService {
private Preferences prefs = null;
public static final String GCM_ONEOFF_TAG = "oneoff|[0,0]";
public static final String GCM_REPEAT_TAG = "komunalRepeat|[7200,1800]";
private static final String TAG = JobDispacherService.class.getSimpleName();
private UplaudPossitionTask uplaudPossitionTask;
#Override
public boolean onStartJob(#NonNull JobParameters job) {
uplaudPossitionTask = new UplaudPossitionTask() {
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
jobFinished(job, false);
}
};
uplaudPossitionTask.execute();
return false; // Answers the question: "Is there still work going on?"
}
#Override
public boolean onStopJob(JobParameters job) {
return true; // Answers the question: "Should this job be retried?"
}
private class UplaudPossitionTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
uploadPosition();
return null;
}
}
}
And I call this service just like this :
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
Job myJob = dispatcher.newJobBuilder()
.setService(JobDispacherService.class)
.setTag("my-unique-tag")
.setRecurring(true)
.setLifetime(Lifetime.FOREVER)
.setTrigger(Trigger.executionWindow(10, (int) 15))
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
dispatcher.mustSchedule(myJob);
I want to repeat this time all the time and I do not have idea why this service is stop wrking when my phone is locked more than 1h
WorkManager :
public static void refreshCouponPeriodicWork() {
//define constraints
Constraints myConstraints = new Constraints.Builder()
.setRequiresDeviceIdle(false)
.setRequiresCharging(false)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.build();
Data source = new Data.Builder()
.putString("workType", "PeriodicTime")
.build();
PeriodicWorkRequest refreshCpnWork =
new PeriodicWorkRequest.Builder(RefreshLatestCouponWorker.class, 10, TimeUnit.HOURS,30, TimeUnit.SECONDS)
.setConstraints(myConstraints)
.setInputData(source)
.build();
WorkManager.getInstance().enqueue(refreshCpnWork);
}
public class RefreshLatestCouponWorker extends Worker {
private Preferences prefs = null;
public RefreshLatestCouponWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
#NonNull
#Override
public Worker.Result doWork() {
//read input argument
String workType = getInputData().getString("workType");
Log.i("refresh cpn work", "type of work request: " + workType);
uploadPosition();
//sending work status to caller
return success();
}
}
I do this for work manager but task run only once and never is repeating
I suggest you read about WorkManager, that will be the correct solution for you. The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts. You can define Periodic requests with specified time interval and device state. Revert in case you find the implementation difficult, i could help you with it.
I have added the latest ACRA:
compile 'ch.acra:acra:4.8.5'
Configured my App class according to docs:
#ReportsCrashes(
reportSenderFactoryClasses = {com.github.dht.screenger.crash.ParseSenderfactory.class}
)
...
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// The following line triggers the initialization of ACRA
ACRA.init(this);
}
Defined the Sender factory:
public class ParseSenderfactory implements ReportSenderFactory {
// NB requires a no arg constructor.
public ReportSender create(Context context, ACRAConfiguration config){
return new ParseSender();
}
}
The sender:
public class ParseSender implements ReportSender {
#Override
public void send(Context context, CrashReportData report) throws ReportSenderException {
// Get the ParseObject to be send to Parse
CrashReportParseObject crashReport = getParseObject(report);
// Associate the current logged user with the crash report
ParseUser currentUser = ParseUser.getCurrentUser();
crashReport.setUser(currentUser);
// Send crash report to Parse
crashReport.saveInBackground();
}
/**
* Get a custom ParseObject for data collected by ACRA.
* #param report
* Report data collected by ACRA.
* #return
* A custom ParseObject containing relevant data.
*/
private CrashReportParseObject getParseObject(Map<ReportField, String> report) {
ReportField[] fields = ACRA.getConfig().customReportContent();
if (fields.length == 0) {
fields = ACRAConstants.DEFAULT_REPORT_FIELDS;
}
CrashReportParseObject finalReport = new CrashReportParseObject();
for (ReportField field : fields) {
finalReport.put(field.toString(), report.get(field));
}
return finalReport;
}
}
And the ParseObject:
#ParseClassName("CrashReport")
public class CrashReportParseObject extends ParseObject {
public String getAndroidVersion(){
return this.getString("ANDROID_VERSION");
}
public String getAppVersionName(){
return this.getString("APP_VERSION_NAME");
}
public String getPhoneModel(){
return this.getString("PHONE_MODEL");
}
public String getProduct(){
return this.getString("PRODUCT");
}
public void setUser(ParseUser user){
this.put("user", user);
}
}
And yet ACRA does not catch an exception I send from the activity:
#Override
public void onStart() {
super.onStart();
throw new RuntimeException("This is a crash");
}
Did I forget something?
UPDATE
Made some progress. Saw the CrashReportParseObject was not being created as Parse wanted the syntax to be ParseObject.create("CrashReport");
Changed the ParseSender to :
public class ParseSender implements ReportSender {
#Override
public void send(Context context, CrashReportData report) throws ReportSenderException {
Log.d("ACRA", "Sending log");
// Get the ParseObject to be send to Parse
ParseObject crashReport = getParseObject(report);
// Associate the current logged user with the crash report
ParseUser currentUser = ParseUser.getCurrentUser();
crashReport.put("user", currentUser);
Log.d("ACRA", crashReport.toString());
// Send crash report to Parse
crashReport.saveInBackground();
}
/**
* Get a custom ParseObject for data collected by ACRA.
*
* #param report Report data collected by ACRA.
* #return A custom ParseObject containing relevant data.
*/
private ParseObject getParseObject(Map<ReportField, String> report) {
ReportField[] fields = ACRA.getConfig().customReportContent();
if (fields.length == 0) {
fields = ACRAConstants.DEFAULT_REPORT_FIELDS;
}
ParseObject finalReport = new ParseObject("CrashReport");
for (ReportField field : fields) {
if (field.toString() != null && report.get(field) != null) {
finalReport.put(field.toString(), report.get(field));
}
}
return finalReport;
}
}
and removed CrashReportParseObject all together. Now I am getting "ACRA caught a RuntimeException for com.github.dht.screenger" but still no object is being created on parse.com. Is it a timing thing? does the app crash before it gets the time save the object?
I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}