Edit ***は、元の質問に対する回答をまだ探しています。ユーザーに再度支払いをさせるのではなく、アイテムが以前に購入されたことを Android が認識しないのはなぜですか? SharedPreferences を設定することは問題ありませんが、ユーザーがアンインストールするとどうなるでしょうか? 彼らは再び買わなければならないでしょう。私はこれをユーザーに望んでいません。ありがとうございました。
アプリが正しくインベントリされない理由を突き止めようとしました。これを TrivialDrive から取得して、自分のものにしようとしました。すべての消費を削除したと思いますが、購入できるアイテムは 1 つだけです。プレミアムアップグレード。これは 1 回だけ購入する必要があり、2 回請求されることはありません。タブレットでアプリをコンパイルして実行すると、購入プロセスに合格し、タスク マネージャーを使用してアプリを閉じて再度開くまで、すべて問題ないように見えます。再度開くと、それを渡してプレミアム機能を実行する代わりに、プレミアムを再度購入するように求められます。
コードは次のとおりです。
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);
}
}