编辑*** 仍然在寻找我原来问题的答案。为什么Android不会看到之前购买的商品而不是让用户再次付款?设置SharedPreferences是一个好主意,但如果用户卸载该怎么办?他们将不得不再次购买。我不希望这个用户。谢谢。
我试图弄清楚为什么应用程序没有正确库存,我从TrivialDrive中取出并尝试将其设为我自己的。我相信我删除了所有的消费,我只有一件商品可以购买;高级升级。这应该只买一次,从不收取两次。当我在平板电脑上编译并运行应用程序时,我通过购买流程确定并且一切顺利,直到我使用任务管理器关闭应用程序并重新打开它。一旦重新打开,它就会要求再次购买保险费,而不是通过并执行高级功能。
以下是代码:
public class myClass extends SherlockActivity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mPlanetTitles;
public static final int DIALOG_DOWNLOAD_PROGRESS = 0;
ProgressBar pd = null;
private ProgressDialog mProgressDialog;
Spinner spLoadFrom;
WebView wv;
private LinearLayout container;
private Button nextButton, closeButton;
private EditText findBox;
private static final String TAG = "Web";
IabHelper mHelper;
static boolean mIsPremium = false;
boolean mIsUserPremium = false;
boolean searchAllowed = false;
static final String PREM_SKU = "prem";
private ArrayAdapter<CharSequence> spinnerArrayAdapter;
String name_free[] = { };
String displayName_free[] = { };
String name_premium[] = { };
String displayName_premium[] = {};
/** Called when the activity is first created. */
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mTitle = mDrawerTitle = getTitle();
mPlanetTitles = getResources().getStringArray(
R.array.fMenu);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// set a custom shadow that overlays the main content when the drawer
// opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,
GravityCompat.START);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
supportInvalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
public void onDrawerOpened(View drawerView) {
getSupportActionBar().setTitle(mDrawerTitle);
supportInvalidateOptionsMenu(); // creates call to
// onPrepareOptionsMenu()
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (savedInstanceState == null) {
selectItem(0);
}
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
wv = (WebView) findViewById(R.id.webview);
pd = (ProgressBar) findViewById(R.id.pBar);
spLoadFrom = (Spinner) findViewById(R.id.Spinner02);
if (mIsPremium == true) {
spinnerArrayAdapter = new ArrayAdapter<CharSequence>(this,
android.R.layout.simple_spinner_item, displayName_premium);
} else {
spinnerArrayAdapter = new ArrayAdapter<CharSequence>(this,
android.R.layout.simple_spinner_item, displayName_free);
}
spinnerArrayAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spLoadFrom.setAdapter(spinnerArrayAdapter);
SpinnerListener spListener = new SpinnerListener();
spLoadFrom.setOnItemSelectedListener(spListener);
String base64EncodedPublicKey = "hidden";
mHelper = new IabHelper(this, base64EncodedPublicKey);
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
@Override
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
complain("Problem setting up in-app billing: " + result);
return;
}
// Hooray, 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);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.menu3, menu);
return super.onCreateOptionsMenu(menu);
}
/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content
// view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(getMenuItem(item))) {
return true;
}
int itemId = item.getItemId();
if (itemId == R.id.search) {
if (searchAllowed == false) {
} else if (itemId == R.id.contact) {
emailme();
} else if (itemId == R.id.rate) {
Uri uri = Uri.parse("market://details?id=" + getPackageName());
Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
startActivity(myAppLinkToMarket);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "Unable to find Play Market",
Toast.LENGTH_SHORT).show();
}
}
return super.onOptionsItemSelected(item);
}
public void buyPrem() {
Log.d(TAG,
"Upgrade button clicked; launching purchase flow for upgrade.");
/*
* 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 = "";
mHelper.launchPurchaseFlow(this, PREM_SKU, 10001,
mPurchaseFinishedListener, payload);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + ","
+ data);
// 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() {
@Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: "
+ purchase);
if (result.isFailure()) {
complain("Error purchasing: " + result);
// Handle error
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(PREM_SKU)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert("Thank you for upgrading to premium!");
mIsPremium = true;
mIsUserPremium = true;
searchAllowed = true;
}
}
};
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
/*if (inventory.hasPurchase(PREM_SKU)) {
mHelper.consumeAsync(inventory.getPurchase(PREM_SKU), null);
}*/
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inventory.getPurchase(PREM_SKU);
mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
if (mIsPremium) {
searchAllowed = true;
mIsUserPremium = true;
Log.d(TAG, "Should be premium by now...");
}
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
/*
* TODO: verify that the developer payload of the purchase is correct.
* It will be the same one that you sent when initiating the purchase.
*
* WARNING: Locally generating a random string when starting a purchase
* and verifying it here might seem like a good approach, but this will
* fail in the case where the user purchases an item on one device and
* then uses your app on a different device, because on the other device
* you will not have access to the random string you originally
* generated.
*
* So a good developer payload has these characteristics:
*
* 1. If two different users purchase an item, the payload is different
* between them, so that one user's purchase can't be replayed to
* another user.
*
* 2. The payload must be such that you can verify it even when the app
* wasn't the one who initiated the purchase flow (so that items
* purchased by the user on one device work on other devices owned by
* the user).
*
* Using your own server to store and verify developer payloads across
* app installations is recommended.
*/
return true;
}
/*public void consumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}*/
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
searchAllowed = false;
//mHelper.consumeAsync(inventory.getPurchase(PREM_SKU),
// mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
@Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
} else {
// handle error
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null)
mHelper.dispose();
mHelper = null;
}
void complain(String message) {
Log.e(TAG, "**** TrivialDrive Error: " + message);
alert("Error: " + message);
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(TAG, "Showing alert dialog: " + message);
bld.create().show();
}
private android.view.MenuItem getMenuItem(final MenuItem item) {
return new android.view.MenuItem() {
@Override
public int getItemId() {
return item.getItemId();
}
public boolean isEnabled() {
return true;
}
@Override
public boolean collapseActionView() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean expandActionView() {
// TODO Auto-generated method stub
return false;
}
@Override
public ActionProvider getActionProvider() {
// TODO Auto-generated method stub
return null;
}
@Override
public View getActionView() {
// TODO Auto-generated method stub
return null;
}
@Override
public char getAlphabeticShortcut() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getGroupId() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Drawable getIcon() {
// TODO Auto-generated method stub
return null;
}
@Override
public Intent getIntent() {
// TODO Auto-generated method stub
return null;
}
@Override
public ContextMenuInfo getMenuInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public char getNumericShortcut() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getOrder() {
// TODO Auto-generated method stub
return 0;
}
@Override
public SubMenu getSubMenu() {
// TODO Auto-generated method stub
return null;
}
@Override
public CharSequence getTitle() {
// TODO Auto-generated method stub
return null;
}
@Override
public CharSequence getTitleCondensed() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean hasSubMenu() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isActionViewExpanded() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCheckable() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isChecked() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isVisible() {
// TODO Auto-generated method stub
return false;
}
@Override
public android.view.MenuItem setActionProvider(
ActionProvider actionProvider) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setActionView(View view) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setActionView(int resId) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setAlphabeticShortcut(char alphaChar) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setCheckable(boolean checkable) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setChecked(boolean checked) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setEnabled(boolean enabled) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setIcon(Drawable icon) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setIcon(int iconRes) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setIntent(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setNumericShortcut(char numericChar) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setOnActionExpandListener(
OnActionExpandListener listener) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setOnMenuItemClickListener(
OnMenuItemClickListener menuItemClickListener) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setShortcut(char numericChar,
char alphaChar) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setShowAsAction(int actionEnum) {
// TODO Auto-generated method stub
}
@Override
public android.view.MenuItem setShowAsActionFlags(int actionEnum) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setTitle(CharSequence title) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setTitle(int title) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setTitleCondensed(CharSequence title) {
// TODO Auto-generated method stub
return null;
}
@Override
public android.view.MenuItem setVisible(boolean visible) {
// TODO Auto-generated method stub
return null;
}
};
}
private class DrawerItemClickListener implements
ListView.OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
switch (position) {
case 0:
mDrawerLayout.closeDrawer(mDrawerList);
break;
case 1:
Intent c1 = new Intent(getBaseContext(), 1.class);
startActivity(c1);
break;
case 2:
Intent c2 = new Intent(getBaseContext(), 5.class);
startActivity(c2);
break;
case 3:
Intent c3 = new Intent(getBaseContext(), 3.class);
startActivity(c3);
break;
case 4:
Intent c4 = new Intent(getBaseContext(), 4.class);
startActivity(c4);
break;
default:
}
}
}
public void selectItem(int position) {
switch (position) {
case 0:
break;
case 1:
setContentView(R.layout.1);
break;
case 2:
setContentView(R.layout.2);
break;
case 3:
setContentView(R.layout.3);
break;
case 4:
setContentView(R.layout.4);
break;
default:
}
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getSupportActionBar().setTitle(mTitle);
}
/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
}
答案 0 :(得分:2)
关闭应用程序,删除所有变量,包括mIsPremium。
因此,你需要在某处保存mIsPremium的值,并在再次打开应用程序时加载它。
我建议您使用SharedPreferences执行此任务。
你可以这样做:
在 IabHelper.OnIabPurchaseFinishedListener 中,您需要保存自己的偏好:
if (purchase.getSku().equals(PREM_SKU)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert("Thank you for upgrading to premium!");
mIsPremium = true;
mIsUserPremium = true;
searchAllowed = true;
SharedPreferences prefs = this.getBaseContext().getSharedPreferences(
"com.example.yourapp", 0);
prefs.edit().putBoolean("premium", true).apply;
}
在用户成功购买保费后,您可以将保费状态“true”保存到共享偏好“premium”。
在 onCreate()方法中,您需要加载首选项:
SharedPreferences prefs = this.getBaseContext().getSharedPreferences(
"com.example.yourapp", 0);
mIsPremium = prefs.getBoolean("premium", false);
在这里加载首选项“premium”,如果此偏好尚不存在,则将采用“false”als默认值。
答案 1 :(得分:2)
虽然@Philip Sheard和@ Al0x提供的答案都非常出色,但我的问题通过审查我的代码来解决。我完全删除了对“消费”过程的任何引用。这是一个简单的修复,但我也整合了SharedPreferences,一切都很好。感谢所有的建议 - 你带我到了正确的地方!
答案 2 :(得分:1)
另一种解决方案是在应用内商店中添加一个按钮,允许用户恢复所有购买。 IAB v3文档中描述了此过程,Apple要求采用这种方法。