Skip to content

Instantly share code, notes, and snippets.

@efemoney
Created December 5, 2017 17:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save efemoney/e03ea19e10fa106e3bb79dd06dd2eef0 to your computer and use it in GitHub Desktop.
Save efemoney/e03ea19e10fa106e3bb79dd06dd2eef0 to your computer and use it in GitHub Desktop.
package com.devone.haute.ui;
import android.app.ActivityManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.devone.haute.BuildConfig;
import com.devone.haute.R;
import com.devone.haute.data.RadioIds;
import com.devone.haute.data.Subscription;
import com.devone.haute.data.model.Promo;
import com.devone.haute.databinding.DialogEnterCouponBinding;
import com.devone.haute.databinding.SubscriptionCardBinding;
import com.devone.haute.firebase.Firebase;
import com.devone.haute.ui.extra.BulletSpan;
import com.devone.haute.ui.extra.HorizontalLineSpan;
import com.devone.haute.ui.widget.SubrcriptionRadioGroup;
import com.devone.haute.ui.widget.SubscriptionFontButton;
import com.devone.haute.ui.widget.SubscriptionRadioButton;
import com.devone.haute.ui.widget.TitleLogoToolbar;
import com.devone.haute.util.FontCache;
import com.devone.haute.util.FontPath;
import com.devone.haute.util.IabBroadcastReceiver;
import com.devone.haute.util.IabHelper;
import com.devone.haute.util.IabResult;
import com.devone.haute.util.Inventory;
import com.devone.haute.util.Purchase;
import com.devone.haute.util.SimpleTextWatcher;
import com.devone.haute.util.SkuDetails;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.ValueEventListener;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.devone.haute.databinding.SubscriptionCardBinding.inflate;
import static com.devone.haute.firebase.Firebase.getCurrentUserSubOverrideRef;
import static com.devone.haute.util.FontPath.lookup;
public class SubscriptionActivity extends AppCompatActivity implements
IabHelper.OnIabSetupFinishedListener,
IabHelper.OnIabPurchaseFinishedListener,
IabHelper.QueryInventoryFinishedListener,
IabBroadcastReceiver.IabBroadcastListener {
public static final String SKU_SUB_PRO = "haute.subscription.pro.2";
public static final String SKU_SUB_ELITE = "haute.subscription.elite.4";
private static final int REQUEST_PAY = 1000;
int progressCount;
ViewPager mPager;
PagerAdapter mAdapter;
ProgressBar progress;
SubrcriptionRadioGroup mRadios;
private IabHelper helper;
private IabBroadcastReceiver iabBroadcastReceiver;
private ValueEventListener subsListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
hideProgress();
if (dataSnapshot.getValue() == null) return;
Subscription sub = dataSnapshot.getValue(Subscription.class);
if (sub == null) {
Toast.makeText(SubscriptionActivity.this.getApplicationContext(),
R.string.error_no_subscription, Toast.LENGTH_LONG).show();
finish();
return;
}
int subValue = sub.getValue();
int radioId = RadioIds.forSubscriptionValue(subValue);
SubscriptionRadioButton radio = (SubscriptionRadioButton) mRadios.findViewById(radioId);
if (radio != null) {
mRadios.clearSubscribed(); // Clear previous subscribed
radio.setSubscribed(true);
}
if (mAdapter.map != null && mAdapter.map.get(subValue) != null) {
SubscriptionFontButton button = mAdapter.map.get(subValue).subButton;
if (button != null) {
button.setEnabled(false);
button.setSubscribed(true);
}
}
mPager.setCurrentItem(subValue >> 1, false); // Update pager position
}
@Override
public void onCancelled(DatabaseError firebaseError) {
hideProgress();
Toast.makeText(SubscriptionActivity.this, "Error: " + firebaseError.getMessage(), Toast.LENGTH_LONG).show();
hideProgress();
}
};
private OnSubscriptionClickListener listener = new OnSubscriptionClickListener() {
@Override
public void onSubscriptionClicked(final int value) {
final Purchase current = mAdapter.getCurrentPurchase();
DatabaseReference ref = Firebase.getCurrentUserSubOverrideRef();
Firebase.addListenerForOneUpdate(ref, new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() == null) {
doNormalOnSUbscribe(value, current);
return;
}
long time = dataSnapshot.getValue(long.class);
if (time < System.currentTimeMillis()) {
doNormalOnSUbscribe(value, current);
return;
}
showCannotSubscribeDialog();
}
@Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(
SubscriptionActivity.this,
"Error retrieving subscriptions: " + databaseError.getMessage(),
Toast.LENGTH_SHORT
).show();
doNormalOnSUbscribe(value, current);
}
});
}
};
private void showCannotSubscribeDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Haute_Dialog);
TitleLogoToolbar title = new TitleLogoToolbar(builder.getContext());
title.setTitleTextAppearance(builder.getContext(), R.style.MuliTextAppearance_Dialog_Title);
title.setTitle(R.string.cannot_subscribe);
builder.setCustomTitle(title).setView(R.layout.dialog_cannot_sub).show();
}
private void doNormalOnSUbscribe(int value, Purchase current) {
if (value == Subscription.starter && current != null) {
showCancelSubDialog();
return;
}
if (value == Subscription.starter) {
subscribeFor(Subscription.STARTER);
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
return;
}
String sku = value == Subscription.pro ? SKU_SUB_PRO : SKU_SUB_ELITE;
try {
showProgress();
helper.launchSubscriptionPurchaseFlow(
SubscriptionActivity.this, sku,
current == null ? null : Arrays.asList(current.getSku()),
REQUEST_PAY,
SubscriptionActivity.this,
getExtraDataForUser()
);
} catch (IabHelper.IabAsyncInProgressException e) {
hideProgress();
e.printStackTrace();
}
}
@SuppressWarnings("ConstantConditions") @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) setTaskDescription(
new ActivityManager.TaskDescription(null, null,
ContextCompat.getColor(this, R.color.colorBackgroundSub))
);
Firebase.init();
setContentView(R.layout.activity_subscription);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null) {
toolbar.inflateMenu(R.menu.menu_coupon);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
SubscriptionActivity.this.showCouponDialog();
return true;
}
});
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SubscriptionActivity.this.finish();
}
});
}
progress = (ProgressBar) findViewById(R.id.progress);
mPager = (ViewPager) findViewById(R.id.pager);
mRadios = (SubrcriptionRadioGroup) findViewById(R.id.radios);
mPager.setOffscreenPageLimit(2);
mRadios.clearCheck();
setupPagerWithRadios();
showProgress();
DatabaseReference ref = getCurrentUserSubOverrideRef();
if (ref == null) { initBilling(); return; }
Firebase.addListenerForOneUpdate(ref, new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() == null) {
initBilling();
return;
}
long time = dataSnapshot.getValue(long.class);
if (time < System.currentTimeMillis()) {
initBilling();
return;
}
initAdapter(null, null, null); // We have no purchase data
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(SubscriptionActivity.this, "Error retrieving subscriptions", Toast.LENGTH_SHORT).show();
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
}
});
}
private void showCouponDialog() {
LayoutInflater inf = LayoutInflater.from(this);
final DialogEnterCouponBinding binding = DialogEnterCouponBinding.inflate(inf, null, false);
final AlertDialog d = new AlertDialog.Builder(this, R.style.Haute_Dialog)
.setView(binding.getRoot())
.show();
binding.coupon.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
binding.positive.setEnabled(!TextUtils.isEmpty(editable));
}
});
binding.positive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DatabaseReference ref = Firebase.getCurrentUserSubOverrideRef();
Firebase.addListenerForOneUpdate(ref, new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() == null) {
redeemCoupon(d, binding, binding.coupon.getText().toString());
return;
}
long time = dataSnapshot.getValue(long.class);
if (time < System.currentTimeMillis()) {
redeemCoupon(d, binding, binding.coupon.getText().toString());
return;
}
showCannotSubscribeDialog();
}
@Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(
SubscriptionActivity.this,
"Error retrieving subscriptions: " + databaseError.getMessage(),
Toast.LENGTH_SHORT
).show();
}
});
}
});
}
private void redeemCoupon(final AlertDialog d, final DialogEnterCouponBinding binding, final String coupon) {
binding.coupon.getText().clear();
binding.coupon.setVisibility(View.GONE);
binding.label.setVisibility(View.GONE);
binding.negative.setVisibility(View.GONE);
binding.positive.setVisibility(View.GONE);
binding.progress.setVisibility(View.VISIBLE);
DatabaseReference coupRef = Firebase.getRef("promos", coupon.toUpperCase());
if (coupRef == null) {
showCouponError(d, binding, "Cannot find the specified promo.");
return;
}
Firebase.addListenerForOneUpdate(coupRef, new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
binding.progress.setVisibility(View.GONE);
if (dataSnapshot.getValue() == null) {
showCouponError(d, binding, "The specified promotion does not exist.");
return;
}
final Promo promo = dataSnapshot.getValue(Promo.class);
binding.title.setVisibility(View.VISIBLE);
binding.title.setText(promo.getName());
binding.desc.setVisibility(View.VISIBLE);
binding.desc.setText(promo.getDescription());
binding.positive.setVisibility(View.VISIBLE);
binding.positive.setEnabled(true);
binding.positive.setText("Confirm");
binding.positive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//noinspection WrongConstant
Subscription sub = Subscription.forValue(promo.getSubValue());
DatabaseReference ref = Firebase.getCurrentUserSubOverrideRef();
if (ref == null) {
showCouponError(d, binding, "Could not find user.");
return;
}
long now = System.currentTimeMillis();
long duration = TimeUnit.DAYS.toMillis(promo.getDurationDays());
long then = now + duration;
ref.setValue(then);
subscribeFor(sub);
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
d.dismiss();
}
});
}
@Override
public void onCancelled(DatabaseError databaseError) {
binding.progress.setVisibility(View.GONE);
showCouponError(d, binding, "Cannot find the specified promo. Error: " + databaseError.getMessage());
}
});
}
private void showCouponError(final AlertDialog d, DialogEnterCouponBinding binding, String message) {
binding.progress.setVisibility(View.GONE);
binding.positive.setVisibility(View.GONE);
binding.title.setVisibility(View.GONE);
binding.desc.setVisibility(View.GONE);
binding.status.setVisibility(View.VISIBLE);
binding.negative.setVisibility(View.VISIBLE);
binding.status.setText(message);
binding.negative.setText("OK");
binding.negative.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
d.dismiss();
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (iabBroadcastReceiver != null) unregisterReceiver(iabBroadcastReceiver);
if (helper != null) helper.disposeWhenFinished();
helper = null;
}
private void setupPagerWithRadios() {
mPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(final int position) {
mRadios.check(position == 0 ? R.id.starter : position == 1 ? R.id.pro : R.id.elite);
}
});
mRadios.setOnCheckedChangeListener(new SubrcriptionRadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(SubrcriptionRadioGroup group, int checkedId) {
mPager.setCurrentItem(checkedId == R.id.starter ? 0 : checkedId == R.id.pro ? 1 : 2, true);
}
});
mPager.setPageTransformer(false, new ViewPager.PageTransformer() {
@Override
public void transformPage(View view, float position) {
if (position < -1) {
// [-Infinity,-1) This page is way off-screen to the left.
view.setAlpha(0.54f);
} else if (position <= 1) {
// [-1,1]
view.setAlpha(1 - (0.46f * (Math.abs(position))));
} else {
// (1,+Infinity] This page is way off-screen to the right.
view.setAlpha(0.54f);
}
}
});
}
private void initBilling() {
helper = new IabHelper(this, Subscription.B64ENCODED_PUBLIC_KEY);
helper.enableDebugLogging(BuildConfig.DEBUG, "HauteIabHelper");
helper.startSetup(this);
}
private void initBillingWithoutUpdate() {
}
private String getExtraDataForUser() {
// Returns UID for now. Uid is unique per user
return Firebase.getCurrentUser().getUid();
}
private Purchase getCurrentPurchase(Inventory inv) {
Purchase pro = inv.getPurchase(SKU_SUB_PRO);
Purchase elite = inv.getPurchase(SKU_SUB_ELITE);
if (pro != null && elite != null) {
Toast.makeText(this, "You have multiple subscriptions.", Toast.LENGTH_SHORT).show();
}
return elite != null ? elite : pro != null ? pro : null;
}
private void subscribeFor(Subscription sub) {
//noinspection ConstantConditions
Firebase.getCurrentUserSubRef().setValue(sub);
}
private void showCancelSubDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Haute_Dialog);
TitleLogoToolbar title = new TitleLogoToolbar(builder.getContext());
title.setTitleTextAppearance(builder.getContext(), R.style.MuliTextAppearance_Dialog_Title);
title.setTitle(R.string.cancel_sub);
AlertDialog dialog = builder.setCustomTitle(title)
.setView(R.layout.dialog_cancel_sub)
.setPositiveButton("Open Play Store", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog12, int which) {
Uri playStoreUri = Uri.parse("https://play.google.com/store/account");
SubscriptionActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, playStoreUri));
}
})
.setNeutralButton("Not Now", null)
.create()
;
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog1) {
Context context = ((AlertDialog) dialog1).getContext();
Button positive = ((AlertDialog) dialog1).getButton(DialogInterface.BUTTON_POSITIVE);
Button neutral = ((AlertDialog) dialog1).getButton(DialogInterface.BUTTON_NEUTRAL);
Typeface muliFont = FontCache.get(context, lookup(FontPath.muli, Typeface.NORMAL));
positive.setTypeface(muliFont);
neutral.setTypeface(muliFont);
}
});
dialog.show();
}
private void alert(String message) {
new AlertDialog.Builder(this, R.style.Haute_Dialog)
.setMessage(message)
.setPositiveButton("OK", null)
.show()
;
}
private void showProgress() {
if (progress != null && progressCount == 0) progress.setVisibility(View.VISIBLE);
progressCount++;
if (progressCount < 0) progressCount = 0;
}
private void hideProgress() {
progressCount--;
if (progress != null && progressCount == 0) progress.setVisibility(View.GONE);
if (progressCount < 0) progressCount = 0;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (helper != null && helper.handleActivityResult(requestCode, resultCode, data)) {
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onIabSetupFinished(IabResult result) {
hideProgress();
// Have we been disposed of in the meantime? If so, quit.
if (helper == null) return;
if (result.isFailure()) {
alert("Problem setting up in-app billing: " + result);
initAdapter(null, null, null);
return;
}
// Important: Dynamically register for broadcast messages about updated purchases.
// We register the receiver here instead of as a <receiver> in the Manifest
// because we always call getPurchases() at startup, so therefore we can ignore
// any broadcasts sent while the app isn't running.
// Note: registering this listener in an Activity is a bad idea, but is done here
// because this is a SAMPLE. Regardless, the receiver must be registered after
// IabHelper is setup, but before first call to getPurchases().
iabBroadcastReceiver = new IabBroadcastReceiver(SubscriptionActivity.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(iabBroadcastReceiver, broadcastFilter);
// IAB is fully set up. Now, let's get an inventory of stuff we own.
try {
showProgress();
helper.queryInventoryAsync(
true, //
null,
Arrays.asList(SKU_SUB_PRO, SKU_SUB_ELITE),
this
);
} catch (IabHelper.IabAsyncInProgressException e) {
hideProgress();
alert("Error querying inventory. Another async operation in progress.");
}
}
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
hideProgress();
// Have we been disposed of in the meantime? If so, quit.
if (helper == null) return;
if (result.isFailure()) {
alert("Failed to query inventory: " + result);
initAdapter(null, null, null);
return;
}
Purchase purchase = getCurrentPurchase(inv);
SkuDetails pro = inv.getSkuDetails(SKU_SUB_PRO);
SkuDetails elite = inv.getSkuDetails(SKU_SUB_ELITE);
initAdapter(pro, elite, purchase);
Subscription sub = Subscription.STARTER;
if (purchase != null && purchase.getPurchaseState() == 0) {
if (purchase.getSku().equals(SKU_SUB_PRO)) sub = Subscription.PRO;
if (purchase.getSku().equals(SKU_SUB_ELITE)) sub = Subscription.ELITE;
}
subscribeFor(sub); // Update subscription state
//Update UI
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
}
private void initAdapter(SkuDetails pro, SkuDetails elite, Purchase purchase) {
mAdapter = new PagerAdapter(pro, elite);
mAdapter.setCurrentPurchase(purchase);
mPager.setAdapter(mAdapter);
}
@Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
hideProgress();
// Have we been disposed of in the meantime? If so, quit.
if (helper == null) return;
if (result.isFailure()) {
alert("Error purchasing: " + result);
return;
}
if (!verifyPayload(purchase)) {
alert("Error purchasing. Authenticity verification failed.");
return;
}
Subscription sub = Subscription.STARTER;
if (purchase.getSku().equals(SKU_SUB_PRO)) sub = Subscription.PRO;
if (purchase.getSku().equals(SKU_SUB_ELITE)) sub = Subscription.ELITE;
// Purchased
if (purchase.getPurchaseState() == 0) subscribeFor(sub);
mAdapter.setCurrentPurchase(purchase);
mAdapter.notifyDataSetChanged();
Firebase.addListenerForOneUpdate(Firebase.getCurrentUserSubRef(), subsListener);
}
@Override
public void receivedBroadcast() {
}
private boolean verifyPayload(Purchase info) {
// Checks that is is this user that actually made the purchase
return android.text.TextUtils.equals(Firebase.getCurrentUser().getUid(), info.getDeveloperPayload());
}
private class PagerAdapter extends android.support.v4.view.PagerAdapter {
static final int NUM_PAGES = 3;
private final String[] headings = new String[]{
"Features Include:",
"Includes all Starter features, and:",
"Includes all Pro features, and:"
};
private final String[] prices = new String[]{
"Free Forever",
"$10/Month",
"$15/Month"
};
private final Pattern patternBullet = Pattern.compile("[a-zA-Z0-9 ]+\\n");
private final Pattern patternLine = Pattern.compile("~");
private final String[] descriptions = new String[]{
"Unlimited Albums\n~\n" +
"5 Customer Profiles\n~\n" +
"Save customers full body measurement\n~\n",
"Unlimited Albums\n~\n" +
"50 Customer Profiles\n~\n" +
"Add watermark when sharing photos\n~\n" +
"Save customers full body measurement\n~\n",
"Unlimited Albums\n~\n" +
"Unlimited Customer Profiles\n~\n" +
"Add watermark when sharing photos\n~\n" +
"Save customers full body measurement\n~\n" +
"Custom measurement templates\n~\n"
};
private final SkuDetails proDetails;
private final SkuDetails eliteDetails;
private Purchase current;
SparseArray<SubscriptionCardBinding> map = new SparseArray<>();
PagerAdapter(SkuDetails proDetails, SkuDetails eliteDetails) {
this.proDetails = proDetails;
this.eliteDetails = eliteDetails;
if (proDetails != null) prices[1] = proDetails.getPrice() + "/Month";
if (eliteDetails != null) prices[2] = eliteDetails.getPrice() + "/Month";
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public int getCount() {
return NUM_PAGES;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = container.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
SubscriptionCardBinding binding = inflate(inflater, container, false);
populate(context, binding, position);
container.addView(binding.getRoot());
return binding.getRoot();
}
private void populate(Context context, SubscriptionCardBinding binding, int position) {
@Subscription.Value int value = 1 << position;
Subscription subscription = Subscription.forValue(value);
String subscriptionName = subscription.getName();
binding.subHead.setText(headings[position]);
binding.subPrice.setText(prices[position]);
binding.subDesc.setText(getSpannedDesc(context, descriptions[position]));
binding.subTitle.setText(getString(R.string.sub_title, subscriptionName));
binding.subButton.setText(getString(R.string.sub_button, subscriptionName));
binding.subButton.setTag(value); // Very important!
binding.subButton.setOnClickListener(listener);
map.put(value, binding);
}
private CharSequence getSpannedDesc(Context context, String desc) {
SpannableStringBuilder spanned = new SpannableStringBuilder(desc);
Matcher matcher = patternLine.matcher(desc);
while (matcher.find()) {
spanned.setSpan(new HorizontalLineSpan(context),
matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher = patternBullet.matcher(desc);
while (matcher.find()) {
spanned.setSpan(new BulletSpan(context),
matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spanned;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
@Subscription.Value int value = 1 << position;
map.remove(value);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
public Purchase getCurrentPurchase() {
return current;
}
public void setCurrentPurchase(Purchase current) {
this.current = current;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment