I trying use Dungeons example in my app. In Android development guide it's written that I should confirm delivery product to user sending CONFIRM_NOTIFICATIONS to market, but I don't see it in example or am I wrong? Should I confirm download and my app should remember if content was successfully delivered?
Where is the best place to invoke downloading, in activity using AsyncTask, in ResponseHandler class or different?
This is something that I've been wondering about today too. From what I can see, in the Dungeons example, when BillingService#purchaseStateChanged is called, it automatically acknowledges all notifications after verifying the purchases.
See lines 506-509 in the example BillingService.java:
if (!notifyList.isEmpty()) {
String[] notifyIds = notifyList.toArray(new String[notifyList.size()]);
confirmNotifications(startId, notifyIds);
}
The solution would appear to be relocating this logic to a place you can manually call when you have completed delivery of your content.
I'm planning to remove that code and make the BillingService#confirmNotifications public so I can call it from my PurchaseObserver implementation when I've delivered my content.
I'll update with the result, but it seems to be a good starting point.
I hope Following Code helps u.
#Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
if (Consts.DEBUG) {
Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState);
}
if (developerPayload == null) {
logProductActivity(itemId, purchaseState.toString());
} else {
logProductActivity(itemId, purchaseState + "\n\t" + developerPayload);
}
if (purchaseState == PurchaseState.PURCHASED) {
mOwnedItems.add(itemId);
Log.v("log_tag", "Item Purchased");
}
mCatalogAdapter.setOwnedItems(mOwnedItems);
mOwnedItemsCursor.requery();
}
In Log if you get "Item Purchased" it indicates that you have successfully download the Item.
Related
I'm using FB AppInvite with a deep link. When I press the "Open" from the invite I get the deep link correctly.
The problem is that once I do that, all subsequent activations of the app are activating the onDeferredAppLinkDataFetched() callback with the same deep-link. It doesn't matter if I launch the app from the launcher or from the overview screen.
Here is the code:
if (targetUrl != null) {
Log.d(TAG, "App Link Target URL: " + targetUrl.toString());
processReferral(targetUrl.toString());
} else {
AppLinkData.fetchDeferredAppLinkData(
activity,
new AppLinkData.CompletionHandler() {
#Override
public void onDeferredAppLinkDataFetched(AppLinkData appLinkData) {
String deeplink = appLinkData.getTargetUri().toString();
Log.w(TAG, "Deferred App Link Target URL: " + deeplink);
// This is happening too aggresively - every activation after one acceptance
processReferral(deeplink);
}
});
}
Will appreciate any clue :-)
I'm trying to implement Facebook sharing in my game using Unity 3D + Facebook Unity SDK. But when I tried testing to post to my wall, this error shows up: "We are Sorry, this post is no longer available. It may have been removed." Can anybody help me? Thanks in advance.
BTW, here's my code:
using UnityEngine;
using System.Collections;
public class FacebookController : MonoBehaviour {
public bool isUsingFacebook = true; //Are we using Facebook SDK? This variable is only
//Feed parameters.
private string link = "market://details?id=com.LaserCookie.Queue"; //The link that will show the user the game's google play store address
private string linkName = "Queue"; //The link name
private string linkCaption = "Wow this game is great! 10/10 must play!"; // The caption of the link
private string linkDescription = "I achieved the score of " + PlayerController.instance.score.ToString() + "! Try to beat me if you can!"; //The description of the link
private string picture = "http://www.drycreekequestriancenter.com/testImage.jpeg"; //This is the image / game icon for the link. For now, it's shamelessly got from a random source. Thank you, random citizen...
void Awake()
{
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
//Init FB
private void Init()
{
if (!FB.IsInitialized)
{
FB.Init(OnInitComplete, OnHideUnity);
}
}
//Callback that will be called when the initialization is completed
private void OnInitComplete()
{
Debug.Log("FB.Init completed: Is user logged in? " + FB.IsLoggedIn);
//Check if we are logged in.
//If not, we will log in.
//If we are, post status.
if (!FB.IsLoggedIn)
{
LoginWithPublish();
}
else
{
PostImpl();
}
}
//Callback that will be called when the game is shown / not
private void OnHideUnity(bool isGameShown)
{
Debug.Log("Is game showing? " + isGameShown);
}
//Post to Facebook. This is the only exposed method because we only use this to post to Facebook.
//The initialization and login will be called when needed.
//It will first detect whether or not we have been initialized. And will init if we haven't.
//Then it will check whether or not we have been logged in with publish. And will log in if not.
public void PostToFacebook()
{
//Are we using facebook SDK?
if (isUsingFacebook)
{
if (!FB.IsInitialized) //Check for initialization
{
Init();
}
else if (!FB.IsLoggedIn) //Check for login
{
LoginWithPublish();
}
else //Post if we are already initia;ized and logged in
{
PostImpl();
}
}
}
//The true implementation of the posting
private void PostImpl()
{
FB.Feed("",link, linkName,linkCaption,linkDescription,picture);
}
//Login to Facebook with publish
private void LoginWithPublish()
{
// It is generally good behavior to split asking for read and publish
// permissions rather than ask for them all at once.
//
// In your own game, consider postponing this call until the moment
// you actually need it.
FB.Login("publish_actions", LoginCallback);
}
//Login callback
void LoginCallback(FBResult result)
{
if (result.Error != null)
{
Debug.Log( "Error Response:\n" + result.Error );
//TODO: Retry login if we have error? Or do we display a pop up?
}
else if (!FB.IsLoggedIn)
{
Debug.Log( "Login cancelled by Player" );
//TODO: Do we display a pop up?
}
else
{
Debug.Log( "Login was successful!" );
PostImpl();
}
}
}
You need to add Key Hash for FB application.
Go to My Apps, select you application, open Setting tab, add platform for android, and add you key hash.
check this link out
Setting a Release Key Hash
I've fixed the issue. It turns out it's because I used my still in development google store address as the link. I thought it would be automatically recognized regardless of my app is live or not. Thank you anyway. :)
With Google Play Game Services, I'm trying to implement a CallBack such that, if there is an issue with sending a message, then I need to resolve it (as each player "passes" their bid to the next player, and all other players need to see what the player that passed bid)
I thought I would try and use the following to instantiate a RealTimeReliableMessageSentListener for that round of messages, so that I can tell if the message was sent and received by everyone:
(I add the tokenID returned by the call to an ArrayList, and then check off remove each tokenID as it comes back in to track when all messages from this round are received)
#Override
public void sendReadyToPlay() {
dLog("Sending ReadyToPlay");
// Broadcast that I'm ready, and see that they are ready
if (!mMultiplayer){
return; // playing against computer
}
// First byte in message indicates whether it's a final score or not
mMsgBuf[0] = (byte) ('R');
readyToPlayTokens.clear();
// Send to every other participant.
for (Participant p : mParticipants) {
dLog("Participant:" + p.getParticipantId());
if (p.getParticipantId().equals(mMyId)) {
continue;}
if (p.getStatus() != Participant.STATUS_JOINED){
continue;
}
readyToPlayTokens.add(mHelper.getGamesClient().sendReliableRealTimeMessage(new RealTimeReliableMessageSentListener() {
#Override
public void onRealTimeMessageSent(int statusCode, int tokenId, String recipientParticipantId){
dLog("onRealTimeMessageSent number two and size is: " + readyToPlayTokens.size());
if(readyToPlayTokens.contains(tokenId)){
readyToPlayTokens.remove(tokenId);
}
dLog("onRealTimeMessageSent number two and size is: " + readyToPlayTokens.size());
if (statusCode != GamesClient.STATUS_OK) {
mGHInterface.onRealTimeMessageReceived("RTPProblem:" + recipientParticipantId);
} else if (readyToPlayTokens.size() == 0) {
mGHInterface.beginRound();
}
}
}, mMsgBuf, mRoomId, p.getParticipantId()));
dLog("sent to:" + p.getParticipantId());
}
}
I can see the messages coming in almost every time from one device to another, so I can see that the messages are going through, BUT, the RealTimeReliableMessageSent listener is only being fired about 50-60 percent of the time, which isn't very reliable! :)
Can anyone see what I might be doing wrong to keep the listener from firing reliably?
may be it happens because you are using anonymous inner class, try to use in different way like implementing your activity RealTimeReliableMessageSentListener
I am working from the Dungeons example that the Android provides, and it feels like I almost got it correctly, but I can't figure out how to detect if the person pressed the back button and backed out of the purchase. Here is what I have so far:
Button buy = (Button)findViewById(R.id.buy);
buy.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
try
{
if (mBillingService.requestPurchase(issueProductId,
Consts.ITEM_TYPE_INAPP , null))
{
// Not sure what to do here. Has the person been sent to the buy screen yet or no?
// There are these strings I need to check for but not entirely certain how:
if(mBillingService.checkBillingSupported(Consts.ITEM_TYPE_INAPP))
{
// OK
}
else
{
// If billing is not supported, give them the item for free
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
try
{
if (mBillingService.requestPurchase(issueProductIdPsych,
Consts.ITEM_TYPE_INAPP , null))
{
// HOW DO I CHECK HERE IF THE USER CANCELED OUT OF THE PURCHASE?
// EVEN ON CANCEL, THEY ARE BEING TAKEN TO THE ITEM THAT THEY NEED TO PAY FOR.
// Send them to the screen of the article.
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
}
catch ( Exception e )
{
// Report exception.
}
});
I added some inline comments where I am confused. I am not certain how to read whether the person purchased the item, or cancelled. If someone could help me with this, that would be great.
Right now this code takes the person to the buy screen, and some people buy, but when people press cancel, they still get taken to the item which they didn't buy. :)
EDIT:
By the way, if users already bought the item and want to come back to it, the purchase request state does not change, right? So what method ends up being triggered? I tested with one purchase and I was able to buy access to one screen with an article, but now can not get to it again since the payment screen keeps telling me I already bought that item, and not taking me to the next page.
Here is how my new onStateChanged method looks like:
#Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload)
{
if (purchaseState == PurchaseState.PURCHASED)
{
mOwnedItems.add(itemId);
if ( itemId != null && itemId.trim().equals("3") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
if ( itemId != null && itemId.trim().equals("4") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, NumberOfBusinessesActivity.class);
}
}
else
if (purchaseState == PurchaseState.CANCELED)
{
// purchase canceled
}
else
if (purchaseState == PurchaseState.REFUNDED)
{
// user ask for a refund
}
else
{
if ( itemId != null && itemId.equals("3") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
if ( itemId != null && itemId.equals("4") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, NumberOfBusinessesActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
}
}
and when I tested it and bought an article, it did get here to the if (purchaseState == PurchaseState.PURCHASED) but not the individual if cases inside it even though the itemId id is "3" or "4"
EDIT:
and this is how I declare the product ids at the beginning of the Activity:
String issueProductIdWebMarketing = "1";
String issueProductIdDonate = "2";
String issueProductIdPsych = "3";
String issueProductIdNumberofBusinesses = "4";
Thanks!!
According to the billing integration guide:
when the requested transaction changes state (for example, the purchase is successfully charged to a credit
card or the user cancels the purchase), the Google Play application sends an IN_APP_NOTIFY broadcast intent.
This message contains a notification ID, which you can use to retrieve the transaction details for the
REQUEST_PURCHASE request.
And the intent com.android.vending.billing.PURCHASE_STATE_CHANGED contains purchaseState
The purchase state of the order. Possible values are 0 (purchased), 1 (canceled), 2 (refunded), or 3
(expired, for subscription purchases only).
source (table 4)
EDIT:
In the dungeons sample, there is an inner class extending PurchaseObserver (see Dungeons activity) that does the job, see especially (onPurchaseStateChange)
EDIT2: some pseudo-code
in the activity (Dungeons class in the sdk sample):
Button buy = (Button)findViewById(R.id.buy);
buy.setEnabled(false);
buy.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
// show purchase dialog and call mBillingService.requestPurchase() on dialog validation
}
});
// request to see if supported
if (!mBillingService.checkBillingSupported()) {
// not supported
}
in the observer (DungeonsPurchaseObserver in the sample):
public void onBillingSupported(boolean supported, String type) {
if (type == null || type.equals(Consts.ITEM_TYPE_INAPP)) {
if (supported) {
// billing supported: enable buy button
} else {
// billing not supported: disable buy button
}
}
}
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
if (purchaseState == PurchaseState.PURCHASED) {
// item purchased
} else if (purchaseState == PurchaseState.CANCELED) {
// purchase canceled
} else if (purchaseState == PurchaseState.REFUND) {
// user ask for a refund
}
}
The problem is that the call mBillingService.requestPurchase is not synchronous. So you can not check right after if the purchase was successful or not.
I recommend you use the library AndroidBillingLibrary. With it you can easily get all the Google Play's events. For that you can use onRequestPurchaseResponse() and onPurchaseStateChanged() from the helpers AbstractBillingActivity or AbstractBillingFragment.
I am trying to test in-app billing in my Android application. The problem is, it appears my market is not up to date since I can't bind to the billing service(I am using the Android example code from here. I keep getting the message This app cannot connect to Market.
Your version of Market may be out of date.
You can continue to use this app but you
won\'t be able to make purchases.
I tried updating the market by opening it, hitting the home, and waiting 5-10 minutes and trying again as outlined here, but it didn't fix the problem. I am testing on a Nexus One with no phone connection - just over WiFi(not sure if this is relevant) with OS 2.2. Has anyone else run into this problem?
Here is the code from my activity:
if (!mBillingService.checkBillingSupported()) {
showDialog(DIALOG_CANNOT_CONNECT_ID);
}
and this is the code from my billing service that is showing the billing is not supported:
public boolean checkBillingSupported() {
return new CheckBillingSupported().runRequest();
}
class CheckBillingSupported extends BillingRequest {
public CheckBillingSupported() {
// This object is never created as a side effect of starting this
// service so we pass -1 as the startId to indicate that we should
// not stop this service after executing this request.
super(-1);
}
#Override
protected long run() throws RemoteException {
Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED");
Bundle response = mService.sendBillingRequest(request);
int responseCode = response.getInt(Consts.BILLING_RESPONSE_RESPONSE_CODE);
if (Consts.DEBUG) {
Log.i(TAG, "CheckBillingSupported response code: " +
ResponseCode.valueOf(responseCode));
}
boolean billingSupported = (responseCode == ResponseCode.RESULT_OK.ordinal());
ResponseHandler.checkBillingSupportedResponse(billingSupported);
return Consts.BILLING_RESPONSE_INVALID_REQUEST_ID;
}
}
private boolean bindToMarketBillingService() {
try {
if (Consts.DEBUG) {
Log.i(TAG, "binding to Market billing service");
}
boolean bindResult = bindService(
new Intent(Consts.MARKET_BILLING_SERVICE_ACTION),
this, // ServiceConnection.
Context.BIND_AUTO_CREATE);
if (bindResult) {
return true;
} else {
Log.e(TAG, "Could not bind to service.");
}
} catch (SecurityException e) {
Log.e(TAG, "Security exception: " + e);
}
return false;
}
Maybe it requires the phone to have a mobile data connection. You can try to install the latest market app manually.