Skip to content
This repository has been archived by the owner on Apr 28, 2022. It is now read-only.

Resolved Issue #129 - Integrated CafeBazaar #492

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions library/src/main/java/org/onepf/oms/OpenIabHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.onepf.oms.appstore.AmazonAppstore;
import org.onepf.oms.appstore.CafeBazaar;
import org.onepf.oms.appstore.FortumoStore;
import org.onepf.oms.appstore.GooglePlay;
import org.onepf.oms.appstore.NokiaStore;
Expand Down Expand Up @@ -204,6 +205,11 @@ public class OpenIabHelper {
*/
public static final String NAME_APTOIDE = "cm.aptoide.pt";

/**
* Internal library name for the CafeBazaar store.
*/
public static final String NAME_CAFEBAZAAR = "com.farsitel.bazaar";

private final PackageManager packageManager;

private final Context context;
Expand Down Expand Up @@ -333,6 +339,38 @@ public Appstore get() {
return new SkubitTestAppstore(context);
}
});

appStorePackageMap.put(CafeBazaar.ANDROID_INSTALLER, NAME_CAFEBAZAAR);
appStoreFactoryMap.put(NAME_CAFEBAZAAR, new AppstoreFactory() {
@NotNull
@Override
public Appstore get() {
final String cafebazaarKey = options.getVerifyMode() != VERIFY_SKIP
? options.getStoreKeys().get(NAME_CAFEBAZAAR)
: null;
return new CafeBazaar(new ContextWrapper(context.getApplicationContext()) {
@Override
public Context getApplicationContext() {
return this;
}

@Override
public boolean bindService(final Intent service, final ServiceConnection conn, final int flags) {
final List<ResolveInfo> infos = getPackageManager().queryIntentServices(service, 0);
if (CollectionUtils.isEmpty(infos)) {
return super.bindService(service, conn, flags);
}
final ResolveInfo serviceInfo = infos.get(0);
final String packageName = serviceInfo.serviceInfo.packageName;
final String className = serviceInfo.serviceInfo.name;
final ComponentName component = new ComponentName(packageName, className);
final Intent explicitIntent = new Intent(service);
explicitIntent.setComponent(component);
return super.bindService(explicitIntent, conn, flags);
}
}, cafebazaarKey);
}
});
}


Expand Down
145 changes: 145 additions & 0 deletions library/src/main/java/org/onepf/oms/appstore/CafeBazaar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package org.onepf.oms.appstore;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.RemoteException;

import com.android.vending.billing.IInAppBillingService;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.onepf.oms.Appstore;
import org.onepf.oms.AppstoreInAppBillingService;
import org.onepf.oms.DefaultAppstore;
import org.onepf.oms.OpenIabHelper;
import org.onepf.oms.appstore.cafebazaarUtils.IabHelper;
import org.onepf.oms.util.CollectionUtils;
import org.onepf.oms.util.Logger;
import org.onepf.oms.util.Utils;

import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
* CafeBazaar copies one to one the implementation of Google In App Billing v3. Therefore, this implementation of the store is based on the implementation for Google In App Billing done by Ruslan Sayfutdinov on 16.04.13
* @author Sergio R. Lumley
* @since 25.06.15.
* @see <a href="https://cafebazaar.ir/developers/docs/iab/implementation/?l=en">Cafe Bazaar implementation details</a>
*/
public class CafeBazaar extends DefaultAppstore {

public static final String VENDING_ACTION = "ir.cafebazaar.pardakht.InAppBillingService.BIND";
public static final String ANDROID_INSTALLER = "com.farsitel.bazaar";

private final Context context;
private IabHelper mBillingService;
private final String publicKey;

// isDebugMode = true |-> always returns app installed via Cafe Bazaar store
private final boolean isDebugMode = Boolean.parseBoolean("false"); // Avoid warnings by parsing its string value

@Nullable
private volatile Boolean billingAvailable = null; // undefined until isBillingAvailable() is called

public CafeBazaar(Context context, String publicKey) {
this.context = context;
this.publicKey = publicKey;
}

@Override
public boolean isPackageInstaller(final String packageName) {
return isDebugMode || Utils.isPackageInstaller(context, ANDROID_INSTALLER);
}

@Override
public boolean isBillingAvailable(final String packageName) {
Logger.d("isBillingAvailable() packageName: ", packageName);
if (billingAvailable != null) {
return billingAvailable; // return previosly checked result
}

if (Utils.uiThread()) {
throw new IllegalStateException("Must no be called from UI thread.");
}

if (!packageExists(context, ANDROID_INSTALLER)) {
Logger.d("isBillingAvailable() Cafe Bazaar is not available.");
// don't set billingAvailable variable in case Cafe Bazaar gets installed later
return false;
}

final Intent intent = new Intent(VENDING_ACTION);
intent.setPackage(ANDROID_INSTALLER);
final List<ResolveInfo> infoList = context.getPackageManager().queryIntentServices(intent, 0);
if (CollectionUtils.isEmpty(infoList)) {
Logger.e("isBillingAvailable() billing service is not available, even though Cafe Bazaar application seems to be installed.");
return false;
}

final CountDownLatch latch = new CountDownLatch(1);
final boolean[] result = new boolean[1];
final ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
final IInAppBillingService mService = IInAppBillingService.Stub.asInterface(service);
try {
final int response = mService.isBillingSupported(3, packageName, IabHelper.ITEM_TYPE_INAPP);
result[0] = response == IabHelper.BILLING_RESPONSE_RESULT_OK;
} catch (RemoteException e) {
result[0] = false;
Logger.e("isBillingAvailable() RemoteException while setting up in-app billing", e);
} finally {
latch.countDown();
context.unbindService(this);
}
Logger.d("isBillingAvailable() Cafe Bazaar result: ", result[0]);
}

public void onServiceDisconnected(ComponentName name) {/*do nothing*/}
};
if (context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
try {
latch.await();
} catch (InterruptedException e) {
Logger.e("isBillingAvailable() InterruptedException while setting up in-app billing", e);
}
} else {
result[0] = false;
Logger.e("isBillingAvailable() billing is not supported. Initialization error.");
}
return (billingAvailable = result[0]);
}

@Override
public int getPackageVersion(final String packageName) {
return Appstore.PACKAGE_VERSION_UNDEFINED;
}

@Override
public String getAppstoreName() {
return OpenIabHelper.NAME_CAFEBAZAAR;
}

@Nullable
@Override
public AppstoreInAppBillingService getInAppBillingService() {
if (mBillingService == null) {
mBillingService = new IabHelper(context, publicKey, this);
}
return mBillingService;
}

private boolean packageExists(@NotNull Context context, String packageName) {
try {
context.getPackageManager().getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException ignored) {
Logger.d(packageName, " package was not found.");
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.onepf.oms.appstore.cafebazaarUtils;

import android.content.Context;
import android.content.Intent;

import org.jetbrains.annotations.NotNull;
import org.onepf.oms.Appstore;
import org.onepf.oms.appstore.CafeBazaar;

/**
* CafeBazaar helper performs the exact same actions as GooglePlay In-App Billing v3. Only requires to change the intent service.
* @author Sergio R. Lumley
* @since 25.06.15.
* @see <a href="https://github.com/congenialmobile/TrivialDrive/commit/395517c8d56f1afba4fb9dbe708a266ce773b0e3">Cafe Bazaar IabHelper changes</a>
*/
public class IabHelper extends org.onepf.oms.appstore.googleUtils.IabHelper {
/**
* Creates an instance. After creation, it will not yet be ready to use. You must perform
* setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not
* block and is safe to call from a UI thread.
*
* @param ctx Your application or Activity context. Needed to bind to the in-app billing service.
* @param base64PublicKey Your application's public key, encoded in base64.
* This is used for verification of purchase signatures. You can find your app's base64-encoded
* public key in your application's page on Google Play Developer Console. Note that this
* is NOT your "developer public key".
* @param appstore TODO
*/
public IabHelper(@NotNull final Context ctx, final String base64PublicKey, final Appstore appstore) {
super(ctx, base64PublicKey, appstore);
}

@Override
protected Intent getServiceIntent() {
final Intent intent = new Intent(CafeBazaar.VENDING_ACTION);
intent.setPackage(CafeBazaar.ANDROID_INSTALLER);
return intent;
}
}