4

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);
}

}
4

3 に答える 3

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」を共有設定「プレミアム」に保存します。

onCreate()メソッドで、設定をロードする必要があります。

SharedPreferences prefs = this.getBaseContext().getSharedPreferences(
        "com.example.yourapp", 0);
mIsPremium = prefs.getBoolean("premium", false);

ここでは、設定「プレミアム」をロードします。この設定がまだ存在しない場合は、デフォルト値の「false」が使用されます。

于 2013-09-08T21:39:33.730 に答える
1

もう 1 つの解決策は、アプリ内ストアにボタンを追加して、ユーザーがすべての購入を復元できるようにすることです。この手順は IAB v3 ドキュメントで説明されており、Apple はそのようなアプローチを義務付けています。

于 2013-09-11T15:03:26.427 に答える