I'm trying to give the user the ability to restore his inapp purchased items from my application.
I'm using the following code:
ServiceConnection mServiceConn = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("TEST", "mService ready to go!");
checkownedItems();
}
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
mService = null;
}
};
public void checkownedItems()
{
try
{
String base64EncodedPublicKey = "MY_KEY";
// Log.d("getPackageName ", "Items Owned: " + getPackageName());
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
if(ownedItems.getInt("RESPONSE_CODE") == 0)
{
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
//ownedSkus.toArray();
ownedSkus.add("test");
for (int n=0;n<ownedSkus.size();n++)
{
Log.d("Items: ", "Items: " + ownedSkus.get(n));
}
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
//Log.d("Items Owned: ", "Items Owned: " + ownedSkus.size()); toast
Log.d("Data List: ", "Data List: " + purchaseDataList.size());
Log.d("signature List: ", "signature List: " + signatureList);
Log.d("continuation Token: ", "continuation Token: " + continuationToken);
if(purchaseDataList.size() > 0)
{
//Item(s) owned
Log.d("purchaseDataList: ", "purchaseDataList: " + purchaseDataList.size());
for(int i=0; i<purchaseDataList.size(); ++i)
{
String sku = ownedSkus.get(i);
/* String xx = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
Log.d(TAG, "INAPP_CONTINUATION_TOKEN: " + xx);
*/
Log.d(TAG, "Item_SKU: " + "consume");
String bookpdf = "http://www.mybook.com/"+sku+ ".pdf";
String Bookvalue= sku.substring(sku.lastIndexOf(".")+1);
downloadAndEncrypt(bookpdf,Bookvalue + ".pdf");
Log.d(TAG, "Item_SKU-Download: "
+ "Download Complete");
Log.d("OWNED_ITEMS_FINAL_SIZE","OWNED_ITEMS_FINAL_SIZE: " + ownedItems.size() );
Log.d("OWNED_ITEMS_FINAL_LIST","OWNED_ITEMS_FINAL_LIST: " + ownedItems.keySet());
Log.d("ownedSkus1","ownedSkus1: " + ownedSkus.size() );
Log.d("ownedSkus2","ownedSkus2: " + ownedSkus.toString());
Log.d("ownedSkus3","ownedSkus3: " + ownedSkus);
//Log.d("testtt","testtt " + "INAPP_PURCHASE_ITEM_LIST".length());
/* String value = ownedItems.getString("INAPP_PURCHASE_ITEM_LIST");
Log.d("my_test","my_test: " + value); */
}
}
else
{
//Item(s) not owned
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result) {
// TODO Auto-generated method stub
if(!result.isSuccess())
{
Log.d("TEST", "In-app Billing setup failed: " + result);
}
else
{
Log.d("TEST", "In-app Billing is set up OK");
}
}
});
}
}
else
{
//Error checking owned items
}
}
catch(RemoteException e)
{
e.printStackTrace();
}
}
#Override
public void onDestroy()
{
super.onDestroy();
if(mServiceConn != null)
{
//unbindService(mServiceConn);
}
}
Note that I'm using the correct base64EncodedPublicKey key of my application.
The problem now is the above code only returns the first inapp item purchased from my application. I tried much everything possible with that piece of code, and it always returns only the first item when trying to log the output results.
Please advise.
Thank you.
Here is the code you are looking for:
private void queryUserPurcahses()
{
mHelper.queryInventoryAsync(mGotInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
boolean is_purchased_1 = inventory.hasPurchase(product_1_ID);
boolean is_purchased_2 = inventory.hasPurchase(product_2_ID);
}
}
};
I figured it out. I was consuming the purchased items in my code. What I did I just commented the consumeitem() function, and when I published my apk (it has to be published to return all the purchased items don't know why!) it worked like a charm and restored all my item that I purchased after commenting the consumeitem().
Thank you all.
Related
I ran YouTube Data API's Android Quickstart code example and an error occurred: null from onCancelled() method. When I wrote a debug log, ChannelListResponse result did not executed. What can I do to solve problem?
#Override
protected List<String> doInBackground(Void... params) {
try {
return getDataFromApi();
} catch (Exception e) {
mLastError = e;
cancel(true);
return null;
}
}
private List<String> getDataFromApi() throws IOException {
Log.d(TAG, "inside getDataFromApi: " + mService);
// Get a list of up to 10 files.
List<String> channelInfo = new ArrayList<String>();
YouTube.Channels.List request = mService.channels()
.list("snippet,contentDetails,statistics");
ChannelListResponse result = request.setForUsername("GoogleDevelopers").execute();
Log.d(TAG, "getDataFromApi: " + request);
Log.d(TAG, "getDataFromApi: " + result);
List<Channel> channels = result.getItems();
if (channels != null) {
Channel channel = channels.get(0);
channelInfo.add("This channel's ID is " + channel.getId() + ". " +
"Its title is '" + channel.getSnippet().getTitle() + ", " +
"and it has " + channel.getStatistics().getViewCount() + " views.");
}
return channelInfo;
}
#Override
protected void onCancelled() {
mProgress.hide();
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
showGooglePlayServicesAvailabilityErrorDialog(
((GooglePlayServicesAvailabilityIOException) mLastError)
.getConnectionStatusCode());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
startActivityForResult(
((UserRecoverableAuthIOException) mLastError).getIntent(),
MainActivity.REQUEST_AUTHORIZATION);
} else {
mOutputText.setText("The following error occurred:\n"
+ mLastError.getMessage());
}
} else {
mOutputText.setText("Request cancelled.");
}
}
I created a class for working with subscriptions in the store.
In the beginning I receive from the server the list of subscriptions with the prices
The code is working and on most devices there is no problem.
But some users do not recive prices. It's like there's no response from the server. I've studied examples of implementation in Google documentation, but I can not understand where I can have problems in the code.
Part of my code:
public class BillingActivity extends AppCompatActivity {
RelativeLayout imageLayout;
View payButton;
// WebView payWebView;
// TextView useCodeButton;
ProgressBar progress1;
ProgressBar progress2;
ProgressBar progress3;
IInAppBillingService inAppBillingService;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_billing_subscription);
price_1monthTextView = (TextView) findViewById(R.id.price_1monthTextView);
relative_1month = (RelativeLayout) findViewById(R.id.relative_1month);
relative_1month.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (MainActivity.myAccount == null) return;
if (MainActivity.myAccount.getUniqid() == null) return;
if (subscription1m != null) {
try {
Log.d("my", "purchase..." + subscription1m.storeName);
purchaseProduct(subscription1m);
} catch (Exception e) {
e.printStackTrace();
Log.d("my", "purchase error = " + e.toString());
}
}
}
});
progress1 = (ProgressBar) findViewById(R.id.progress1);
startGoogleBilling();
}
private void startGoogleBilling() {
if (serviceConnection != null) {
unbindService(serviceConnection);
}
progress1.setVisibility(View.VISIBLE);
serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
inAppBillingService = IInAppBillingService.Stub.asInterface(service);
getSubscribtionsList();
}
#Override
public void onServiceDisconnected(ComponentName name) {
inAppBillingService = null;
}
};
Intent serviceIntent =
new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private static final int PR_CNT = 3;
List<InAppProduct> subscriptions = null;
InAppProduct subscription1m = null;
InAppProduct subscription3m = null;
InAppProduct subscription1y = null;
String[] productIds = {"eq.subscription.1m", "eq.subscription.3m.2", "eq.subscription.1y"};
private void getSubscribtionsList() {
mSwipeRefreshLayout.setRefreshing(false);
progress1.setVisibility(View.GONE);
try {
subscriptions =
getInAppPurchases("subs", productIds[0], productIds[1], productIds[2]);
if (subscriptions.size() == PR_CNT) {
for (InAppProduct inAppProduct : subscriptions) {
String productId = inAppProduct.productId;
Log.d("my", "productId= " + productId);
if (productId.contains(productIds[0])) subscription1m = inAppProduct;
if (productId.contains(productIds[1])) subscription3m = inAppProduct;
if (productId.contains(productIds[2])) subscription1y = inAppProduct;
}
Log.d("my", "1m= " + subscription1m.storeName + " pr=" + subscription1m.price + "\\n\\r " +
"3m= " + subscription3m.storeName + " pr=" + subscription3m.price + "\\n\\r " +
"1y= " + subscription1y.storeName + " pr=" + subscription1y.price + "\\n\\r ");
///----------------------!!!!
// purchaseProduct(inAppProduct);
}
updatePrices();
} catch (Exception e) {
Log.d("my", "exc = " + e.toString());
if (e.toString().contains("Attempt to invoke interface method 'android.os.Bundle com.android.vending.billing.IInAppBillingService.getSkuDetails")) {
if (attempt < 4) {
//getSubscribtionsList();
// startGoogleBilling();
} else {
}
}
// Toast.makeText(this, "Google inApp connection error", Toast.LENGTH_SHORT).show();
// refreshButton.setVisibility(View.VISIBLE);
startGoogleBilling();
}
}
private int attempt = 0;
#Override
public void onDestroy() {
super.onDestroy();
if (serviceConnection != null) {
unbindService(serviceConnection);
}
}
class InAppProduct {
public String productId;
public String storeName;
public String storeDescription;
public String price;
public boolean isSubscription;
public int priceAmountMicros;
public String currencyIsoCode;
public String getSku() {
return productId;
}
String getType() {
return isSubscription ? "subs" : "inapp";
}
}
List<InAppProduct> getInAppPurchases(String type, String... productIds) throws Exception {
ArrayList<String> skuList = new ArrayList<>(Arrays.asList(productIds));
Bundle query = new Bundle();
query.putStringArrayList("ITEM_ID_LIST", skuList);
Bundle skuDetails = inAppBillingService.getSkuDetails(
3, getPackageName(), type, query);
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
List<InAppProduct> result = new ArrayList<>();
for (String responseItem : responseList) {
JSONObject jsonObject = new JSONObject(responseItem);
InAppProduct product = new InAppProduct();
// "com.example.myapp_testing_inapp1"
product.productId = jsonObject.getString("productId");
// Покупка
product.storeName = jsonObject.getString("title");
// Детали покупки
product.storeDescription = jsonObject.getString("description");
// "0.99USD"
product.price = jsonObject.getString("price");
// "true/false"
product.isSubscription = jsonObject.getString("type").equals("subs");
// "990000" = цена x 1000000
product.priceAmountMicros =
Integer.parseInt(jsonObject.getString("price_amount_micros"));
// USD
product.currencyIsoCode = jsonObject.getString("price_currency_code");
result.add(product);
}
return result;
}
private void updatePrices() {
if (subscriptions.size() == PR_CNT) {
price_1monthTextView.setText(subscription1m.price);
price_3monthTextView.setText(subscription3m.price);
price_1yearTextView.setText(subscription1y.price);
}
}
}
my problem was the bit depth of the X variable priceAmountMicros
The price from playmarket is multiplied by a 1000000 , and Kazakh tenge has exchange rate = 1 USD= 331,3 KZT.
And if i have price 18 000 KTZ and * 1000 000.
For price variable I must use long type
I want to get message in my chat activity without press back button in Quickblox instance chat
userData = new QBUser();
userData.setEmail(uNameStr);
userData.setPassword(uPwdStr);
userData.setId(user.getId());
You have to set listeners after the privateChat.sendMessage(chatMessage); so when you get success in listeners call notifydatasetchange method of the adapter.
private void sendMessage() {
// privateChatManager = chatService.getPrivateChatManager();
Log.d("PRIVATE", ">>>>> " + privateChatManager);
Log.d("INST", ">>>>> " + QBChatService.getInstance());
Log.d("CHAT ", "" + chatService);
Log.d("PRI", ">>>>> " + QBChatService.getInstance().getPrivateChatManager());
//login to chat firstly
if (messageEdt.length() > 0) {
privateChatMessageListener = new QBMessageListener<QBPrivateChat>() {
#Override
public void processMessage(QBPrivateChat privateChat, final QBChatMessage chatMessage) {
Log.e("privateChat ", " " + privateChat);
Log.e("chatMessage", "" + chatMessage);
chatListAdapter.notifyDataSetChanged();
}
#Override
public void processError(QBPrivateChat privateChat, QBChatException error, QBChatMessage originMessage) {
Log.e("privateChat ", " " + privateChat);
Log.e("QBChatMessage", "" + originMessage);
Log.e("error", "" + error);
}
};
privateChatManagerListener = new QBPrivateChatManagerListener() {
#Override
public void chatCreated(final QBPrivateChat privateChat, final boolean createdLocally) {
if (!createdLocally) {
privateChat.addMessageListener(privateChatMessageListener);
}
}
};
ChattingFragment.chatService.getPrivateChatManager().addPrivateChatManagerListener(privateChatManagerListener);
try {
QBChatMessage chatMessage = new QBChatMessage();
chatMessage.setBody(messageEdt.getText().toString());
chatMessage.setProperty("save_to_history", "1"); // Save a message to history
chatMessage.setProperty("notification_type", "1");
chatMessage.setSenderId(Integer.parseInt(Util.ReadSharePrefrence(getApplicationContext(), Constant.SHRED_PR.KEY_QB_USERID)));
chatMessage.setRecipientId(opponentId);
chatMessage.setMarkable(true);
privateChatManager = QBChatService.getInstance().getPrivateChatManager();
QBPrivateChat privateChat = privateChatManager.getChat(opponentId);
if (privateChat == null) {
privateChat = privateChatManager.createChat(opponentId, privateChatMessageListener);
}
// send message
privateChat.sendMessage(chatMessage);
privateChat.addMessageSentListener(privateChatMessageSentListener);
privateChat.addMessageListener(privateChatMessageListener);
} catch (SmackException.NotConnectedException e) {
Toast.makeText(PrivateChat.this, "Exception " + e, Toast.LENGTH_SHORT).show();
}
}
}
private QBMessageSentListener<QBPrivateChat> privateChatMessageSentListener = new QBMessageSentListener<QBPrivateChat>() {
#Override
public void processMessageSent(QBPrivateChat qbChat, QBChatMessage qbChatMessage) {
Log.d("MEHUL", "M " + qbChat);
Log.d("MSG", "MSG " + qbChatMessage);
hashmap = new HashMap<String, String>();
hashmap.put("id", "" + qbChatMessage.getId());
if (qbChatMessage.getBody() != null) {
hashmap.put("msg", "" + qbChatMessage.getBody());
} else {
hashmap.put("msg", "");
}
hashmap.put("recipient_id", "" + opponentId);
hashmap.put("sender_id", "" + user_id);
Collection<QBAttachment> collection = qbChatMessage.getAttachments();
if (collection != null && collection.size() > 0) {
for (QBAttachment attachment : collection) {
String imageid = attachment.getId();
String RecivedUrl = attachment.getUrl();
hashmap.put("url", "" + RecivedUrl);
}
//Here is the AsyncTask where I am trying to download image
//
}
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
// textView is the TextView view that should display it
hashmap.put("updated_at", "" + currentDateTimeString);
chatArraylist.add(hashmap);
Toast.makeText(getApplicationContext(), "Message Sent Successfully", Toast.LENGTH_SHORT).show();
messageEdt.setText("");
chatListAdapter = new PrivateChatMsgListAdapter(getApplicationContext(), chatArraylist);
lvChatDetails.setAdapter(chatListAdapter);
lvChatDetails.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
lvChatDetails.setStackFromBottom(true);
chatListAdapter.notifyDataSetChanged();
scrollMyListViewToBottom();
}
#Override
public void processMessageFailed(QBPrivateChat qbChat, QBChatMessage qbChatMessage) {
Log.d("MEHUL", "M " + qbChat);
Log.d("MSG", "ERR " + qbChatMessage);
}
};
I'm using the socialAuth plugin to connect a user to linkdin within my app. I have the connection set up correctly and retrieves data. However, I'm unsure how I can get my main activity to wait until the socialAuthListeners have fired and finished. I know a little about threading but I haven't used it with listeners before. Here's my code:
public class LinkdinAuth {
private static final String TAG = "TEST";
// SocialAuth Components
SocialAuthAdapter adapter;
ProgressDialog mDialog;
private Context context;
private boolean loggedIn = false;
private Bundle LinkdinData;
public LinkdinAuth(Context C){
this.context = C;
LinkdinData = new Bundle();
adapter = new SocialAuthAdapter(new ResponseListener());
}
public void adapterAuthorize(View v){
adapter.authorize(v.getContext(), Provider.LINKEDIN);
}
private final class ResponseListener implements DialogListener
{
public void onComplete(Bundle values) {
String providerName = values.getString(SocialAuthAdapter.PROVIDER);
Log.d("Main", "providername = " + providerName);
mDialog = new ProgressDialog(context);
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mDialog.setMessage("Loading...");
//Get profile information
adapter.getUserProfileAsync(new ProfileDataListener());
// get Job and Education information
mDialog.show();
adapter.getCareerAsync(new CareerListener());
loggedIn = true;
Log.d("Main", "LOGGED IN = " + loggedIn );
Toast.makeText(context, providerName + " connected", Toast.LENGTH_SHORT).show();
}
#Override
public void onBack() {
Log.d("Main", "Dialog Closed by pressing Back Key");
}
#Override
public void onCancel() {
Log.d("Main", "Cancelled");
}
#Override
public void onError(SocialAuthError e) {
Log.d("Main", "Error");
e.printStackTrace();
}
}
// To receive the profile response after authentication
private final class ProfileDataListener implements SocialAuthListener<Profile> {
#Override
public void onExecute(String provider, Profile t) {
Log.d("Sign Up", "Receiving Data");
mDialog.dismiss();
Profile profileMap = t;
LinkdinData.putString("Validated ID", profileMap.getValidatedId() );
LinkdinData.putString("First Name", profileMap.getFirstName());
LinkdinData.putString("Last Name", profileMap.getLastName());
LinkdinData.putString("Email", profileMap.getEmail());
LinkdinData.putString("Country", profileMap.getCountry());
LinkdinData.putString("Language", profileMap.getLanguage());
LinkdinData.putString("Location", profileMap.getLocation());
LinkdinData.putString("Profile Image URL", profileMap.getProfileImageURL());
}
#Override
public void onError(SocialAuthError arg0) {
// TODO Auto-generated method stub
}
}
private final class CareerListener implements SocialAuthListener<Career> {
#Override
public void onExecute(String provider, Career t) {
Log.d("Custom-UI", "Receiving Data");
mDialog.dismiss();
Career careerMap = t;
//get education
Log.d("Main", "Education:");
if(careerMap.getEducations() != null){
for(Education E: careerMap.getEducations()){
Log.d("Main", "School = " +E.getSchoolName() );
Log.d("Main", "Major = " + E.getFieldOfStudy() );
Log.d("Main", "Degree = " + E.getDegree() );
Log.d("Main", "Start Date = " + E.getStartDate() );
Log.d("Main", "End Date = " + E.getEndDate() );
}
}
Log.d("SignUp", "Career");
if(careerMap.getPositions() != null){
for(Position P: careerMap.getPositions()){
LinkdinData.putString("Company Name", P.getCompanyName() );
LinkdinData.putString("Job Title", P.getTitle() );
Log.d("Main", "Industry = " + P.getIndustry() );
Log.d("Main", "Start Date = " + P.getStartDate() );
Log.d("Main", "End Date = " + P.getEndDate() );
}
}
}
#Override
public void onError(SocialAuthError e) {
}
}
public boolean isLoggedIn(){
return loggedIn;
}
public Bundle getLinkdinData(){
return LinkdinData;
}
So, as you can see. I have 2 listeners that get data after authorization goes through. And my main activity makes creates an instance, calls the adapterAuthroizeMethod and then if the user logs in a flag is set. Then getLinkedData is called. However I would like it to wait until I know the listeners have fired before calling getlinkdinData. Here's what my Main Activity does after a button press:
L.adapterAuthorize(v);
loggedInWithLinkdin = L.isLoggedIn();
Bundle B = L.getLinkdinData();
Intent i = new Intent(getBaseContext(), UserRegistration.class);
i.putExtra("linkdin bundle", B);
//startActivity(i);
Any ideas?
thanks
Well, not a recommend solution but more of a hack.
Here is what you can do.
Wrap the aync call around this construct :
AtomicBoolean done = new AtomicBoolean(false);
Global ans; // the return value holder
try{
result = someAsyncCall(query, new Thread()); // this new thread is for listener callback
result.setResultListener(result -> {
// do something with result.
ans = result.getAns() ; // set global ans
done.set(true);
synchronized (done) {
done.notifyAll(); // notify the main thread which is waiting
}
});
}
catch (Exception e ) {
Log(e);
}
synchronized (done) {
while (done.get() == false) {
done.wait(); // wait here until the listener fires
}
}
return ans; // return global ans
Here is my code
this is my InstantMessage application
the method processMessage will automatically do when the applicaiton have new message
My problem is the textview not work when method processMessage do but when i send message when textview update by both use the same updateChat method
#Override
public void onClick(View v) {
String message = text.getText().toString();
try {
Log.e("On Click", message + " : " + contact);
chat.sendMessage(message);
message = username + " : " + message;
updateChat(message);
} catch (XMPPException e) {
e.printStackTrace();
}
}
public void updateChat(String message){
chatLog += message + "\n";
chatBox.setText(chatLog);
}
#Override
public void processMessage(org.jivesoftware.smack.Chat arg0, Message arg1) {
String temp = InstantMessage.usernameCutter(arg0.getParticipant()) + " : "+ arg1.getBody();
Log.e("ChatPage ", "ProcessMessage");
updateChat(temp);
}
Are processMessage method running on UI thread?
if not, use this construction
final String temp = InstantMessage.usernameCutter(arg0.getParticipant()) + " : "+ arg1.getBody();
runOnUiThread(new Runnable() {
public void run() {
updateChat(temp);
}
});