I want to include in-app purchases to my app, but I cannot bind my activity to the in-app billing service.
I have already done all the steps mentioned in the page https://developer.android.com/training/in-app-billing/preparing-iab-app.html
Debugging in a physical device I found that the issue is in the next command of the IabHelper class:
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
I noticed that it is not working because the program does not stop at either of the two methods of the ServiceConnection instance, that is to say, it does not stop at onServiceConnected() nor onServiceDisconnected()
I made a test using the same command directly in my Activity and the bind with the in-app billing service was successful.
So, the bind is working if it is requested from the Activity but it is not working when it is requested from the IabHelper class.
My question is, how can I bind my activity to the billing service from the IabHelper class?
Here is the code to call the startSetup method from IabHelper:
mHelper = new IabHelper(this, publicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
if (mHelper == null) return;
}
});
This is the code within IabHelper class to bind to service:
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> intentServices = mContext.getPackageManager().queryIntentServices(serviceIntent, 0);
if (intentServices != null && !intentServices.isEmpty())
{
// service available to handle that Intent
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
And here is the code that I used to bind to the service directly from the activity:
private IInAppBillingService mService;
ServiceConnection mServiceConn = new ServiceConnection()
{
#Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
#Override
public void onServiceConnected(ComponentName name,IBinder service)
{
mService = IInAppBillingService.Stub.asInterface(service);
}
};
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
Thanks in advance for your help
I just realize my mistake, I was calling the queryInventoryAsync method before the startSetup get finished.
This was my mistake:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
}
});
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
To correct, I changed the code to:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
else if(result.isSuccess())
{
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
}
}
});
When I tested the bind directly from the activity, I had removed the command for the query, this is the reason it worked from the activity; when I was testing from the IabHelper class I was calling the query command
Related
I'm trying to use Android In App Billing by following google documents. But I tried to use bindService method for using InAppBillingService Object (mService). It returns true, but mService is still null. here is my code
public class PaymentActivity extends AppCompatActivity {
IInAppBillingService mService;
ServiceConnection mServiceConn;
ArrayList<String> skuList;
Bundle querySkus;
Bundle skuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
Log.d("payment", "isBillingAvailable? " + isBillingAvailable(this));
String chargeString = getIntent().getStringExtra("charge");
Log.d("intentTest", "charge is: " + chargeString);
mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Payment", "service disconnected!");
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("Payment", "service connected!");
}
};
}
#Override
protected void onStart() {
super.onStart();
// Bind to IInAppBillingService
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
try{
while(mService == null){
Thread.sleep(1000);
Log.d("payment", "sleep 1 second");
}
}catch (InterruptedException e)
{
e.printStackTrace();
}
skuList = new ArrayList<String> ();
skuList.add("premiumUpgrade");
skuList.add("gas");
querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
public static boolean isBillingAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
List<ResolveInfo> list = packageManager.queryIntentServices(intent, 0);
return list.size() > 0;
}
}
I know it is vert bad code because bindService is called in onCreate. But I tried it on Asyn and there was no change. I tried to wait until connection is completed by sleeping mainTread. This try makes my App in infinited Loop.
my error message is
java.lang.RuntimeException: Unable to start activity ComponentInfo{kr.co.bigsapp.www/kr.co.bigsapp.www.activities.PaymentActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'android.os.Bundle com.android.vending.billing.IInAppBillingService.getSkuDetails(int, java.lang.String, java.lang.String, android.os.Bundle)' on a null object reference
Please help me TT
Your approach (looping until mService is non-null) is flawed. In fact, that's what's preventing the service from being created.
The creation of a local, "in-process" Service does not happen synchronously with the bindService()/startService() call. You have to return control to the framework; in this case, that means returning from onStart(). This is because the state of the Service is advanced by the message queue (Looper) that is running on the main thread, and while you're stuck in onStart(), that Looper isn't "looping".
If you return, you'll find that Service.onCreate() and ServiceConnection.onServiceConnected() are called within a few milliseconds.
I've been working on a music player app. I'm using a service to run the MediaPlayer. From a fragment I start the service using startService(Intent) and then I bound it to my activity. At least that's what I intend to do. The thing is that my app after getting terminated attempts to launch the service again and since the app is already terminated, the service throws an exception.
E/ActivityThread: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here.
My onStartCommand() is getting called 2 times. Although I've been able to stop the crashing message by returning START_NOT_STICKY in onStartCommand() as it was suggested in this link. I would like to understand what is the actual problem here.
My project is available on my GitHub if anyone would like to check the code. Music-Player-App.
I'm using a fragment in my MainActivity to work with the service. Below codes are where I work in between MainFragment and MediaPlayerService.
MainFragment
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
playerService = binder.getService();
serviceBound = true;
Toast.makeText(getActivity(), "Media Player Active", Toast.LENGTH_SHORT).show();
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
public void playAudio(int audioIndex) {
currentFile = audioIndex;
if (!serviceBound) {
// storage = new DataStorage(getActivity());
/* storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
serviceBound = true;
Log.v("TAG", "Creating new instance");
Intent playerIntent = new Intent(getActivity(), MediaPlayerService.class);
getActivity().startService(playerIntent);
getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//storage = new DataStorage(getActivity());
/*storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
Log.v("TAG", "Broadcasting");
getActivity().sendBroadcast(broadcastIntent);
}
Intent playingBroadcast = new Intent(Broadcast_PLAY_BTN_CHANGE);
getActivity().sendBroadcast(playingBroadcast);
Intent nextPlayingBroadcastMain = new Intent(Broadcast_SONG_TEXT_CHANGE);
getActivity().sendBroadcast(nextPlayingBroadcastMain);
}
MediaPlayerService
private void initMediaPlayer(){
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try{
mediaPlayer.setDataSource(currentMedia.getData());
currentFileIndex = MainFragment.currentFile;
MainActivity.durationText.setText(currentMedia.getDuration());
Toast.makeText(getApplicationContext(),"Playlist Size: "+MainFragment.playlist.size() +"\nSong No.: "+(currentFileIndex+1) ,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
#Override
public void onCreate() {
super.onCreate();
callStateListener();
registerAudioOutputChange();
register_playNewAudio();
registerStopMediaBroadcast();
registerUpdatePlaylistReceiver();
registerPlayButtonBroadcast();
registerPrevButtonBroadcast();
registerNextButtonBroadcast();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
playList = new ArrayList<>();
playList = MainFragment.playlist;
currentMedia = MainFragment.playlist.get(MainFragment.currentFile);
}catch (NullPointerException e){
e.printStackTrace();
stopSelf();
}
if(requestAudioFocus() == false)
stopSelf();
if (currentMedia.getData() != null && currentMedia.getData() !="") {
initMediaPlayer();
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer!=null){
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
if(phoneStateListener != null){
telephonyManager.listen(phoneStateListener,PhoneStateListener.LISTEN_NONE);
}
unregisterReceiver(audioOutputChange);
unregisterReceiver(playNewAudio);
unregisterReceiver(stopMediaBroadcast);
unregisterReceiver(updatePlaylistReceiver);
unregisterReceiver(playButtonBroadcast);
unregisterReceiver(prevButtonBroadcast);
unregisterReceiver(nextButtonBroadcast);
//new DataStorage(getApplicationContext()).clearCachedAudioPlaylist();
}
You don't have an unbindService call anywhere in your code. So whenever the Activity gets destroyed, the system detects that it is still bound to a ServiceConnection and has been leaked. This is still the case when calling bindService inside of a Fragment. Since fragments don't inherit from Activity or Context, they don't have a context reference themselves thus they must use their parent Activities context. Remember to always call unbindService when the owning component is being destroyed, whether it's a Fragment, Activity, or even another Service. It's not unheard for a service to bind to another.
If you don't want your bound service to be destroyed when all clients unbind, you need to add special logic to determine if the Service should transition to a started service temporarily so it won't be killed by the OS, and stop the service when a client rebinds to it.
I am having trouble with this and can't get this to work. This is the code for empty main category launcher activity that has to show splash screen if service not running or user not authenticated else start conversations activity.
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
public class EntryPoint extends Activity {
private IAppManager imService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
imService = ((IMService.IMBinder) service).getService();
// this is not starting activity :(
// Start converstion activity if service running and user ok
if (imService.isUserAuthenticated() == true) {
try {
Intent i = new Intent(EntryPoint.this, Conversations.class);
startActivity(i);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// this is not working
// start login activity if service disconnected
public void onServiceDisconnected(ComponentName className) {
imService = null;
try {
Intent intent = new Intent(getBaseContext(), Splash.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Start and bind the imService
startService(new Intent(EntryPoint.this, IMService.class));
}
#Override
protected void onPause() {
super.onPause();
try {
unbindService(mConnection);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
try {
bindService(new Intent(EntryPoint.this, IMService.class),
mConnection, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
e.printStackTrace();
}
}
}
When I run app, neither Conversations is run nor Splash activity is run but instead I see empty activity :( There is no error also, just empty EntryPoint activiy is run which should actually launch one of other activities.
Does anyone know what I am doing wrong here ?
Most probably your service is not connected and thus not able to interact with your activity.
Create an intent separately and then assign it at startService & bindService, instead of creating a new instance of intent every time for startService & bindService.
Also why are you binding the service at onResume()? Check this link out on why you should try to avoid this if possible - Binding to Service in onCreate() or in onResume()
Intent serviceIntent = new Intent(EntryPoint.this, IMService.class);
startService(serviceIntent);
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
When I get UserRecoverableAuthIOException in AbstractThreadedSyncAdapter, I'm creating a notification as below.
Here's how I'm creating the service:
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
Log.i("Service", "Thread started");
return this.getSyncAdapter().getSyncAdapterBinder();
}
private GoogleTasksSyncAdapter getSyncAdapter() {
if (syncAdapter == null)
{
Log.i("Service", "syncAdapter started");
syncAdapter = new MySyncAdapter(this);
}
return syncAdapter;
}
Once the thread is started, I'm raising a notification. But once user clicks on the notification, they can see the authorization activity. After authorising how to resume from the last point. I.e how to get notified once the activity is closed in Syncadapter.
The SyncAdapter thread are running, and you want to get notification when SyncAdapter ends, right?
So, you can comunicate the SyncAdapter thread with BroadCast.
In your SyncAdapter class:
Intent i = new Intent(SYNC_FINISHED);
context.sendBroadcast(i);
Log.i(TAG, "Network synchronization complete");
In a activity or a fragment:
private BroadcastReceiver syncFinishedReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync finished!!");
// Here you can send your notification or another thing that you want
}
};
#Override
public void onStart() {
super.onStart();
getActivity().registerReceiver(syncFinishedReceiver, new IntentFilter(SyncAdapter.SYNC_TASK_FINISHED));
}
#Override
public void onStop() {
super.onStop();
getActivity().unregisterReceiver(syncFinishedReceiver);
}
NOTE: The SYNC_FINISHED constant, you can define previously in your SyncAdapter
I hope I've helped you.
Greetings!
In your SyncAdapter you do something like:
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.i(TAG, "Beginning network synchronization");
if(extras.getBoolean(RUN_METHOD_1) || extras.getBoolean(RUN_ALL)) {
method1();
}
if(extras.getBoolean(RUN_METHOD_2) || extras.getBoolean(RUN_ALL)) {
method2();
}
}
public void method1(){
try{
// do something
} catch (Exception e) {
e.printStackTrace();
// here you can send your notification when exception occours.
}
}
public void method2(){
try{
// do something
} catch (Exception e) {
e.printStackTrace();
// here you can send your notification when exception occours.
}
}
in your "authorization" code you do something like:
Bundle b = new Bundle();
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
b.putBoolean(SyncAdapter.RUN_METHOD_1, true);
ContentResolver.requestSync(account, CONTENT_AUTHORITY, b);
so you can run where the sync stopped.
Greetings!!!
Here is the solution,
we need to use the syncResult.stats.numAuthExceptions to tell about exception, it throws message automatically. syncResult.delayUntil will wait and restart sync after elapsing time
How to show sync failed message
I've searched around for hours trying to figure this out. Here's what I have done so far.
(Note: I'm developing in Android Studio)
Generated a signed APK and uploaded to my developer console
Made an in-app product and activated it
Added the Billing permission to my manifest
Extensively combed Stack to try and find similar problems.
Basically in logcat I see that IABHelper starts setup, but never completes at any time. (the listener never gets a callback)
private static final String TAG = "Preference Activity";
private static final String SKU_PRO = "desk.clock.pro.license";
static final int RC_REQUEST = 10001;
IabHelper mHelper;
private boolean mIsPremium;
private ArrayList<Preference> proSettings;
IInAppBillingService mService;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
bindService(new
Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
proSettings = new ArrayList<Preference>();
ActionBar b = getActionBar();
b.setDisplayHomeAsUpEnabled(true);
colorListener();
textureListener();
bgColorListener();
onPresetListener();
gradListener();
String base64Key = "[my key from dev console]";
bindService(new
Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
mHelper = new IabHelper(this, base64Key);
mHelper.enableDebugLogging(true);
Log.d(TAG, "Starting setup");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
Log.d(TAG, "Setting up success");
Log.d(TAG, "querying inventory");
mHelper.queryInventoryAsync(mGotInventoryListener);
Log.d(TAG, "queried");
}
});
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
mIsPremium = false;
disableProAndRevertSettings();
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PRO);
if(mIsPremium) {
enableProSettings();
}else {
disableProAndRevertSettings();
}
}
}
};
ServiceConnection mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};
//rest of my activity
I get this line in my logcat but never anything after this
10-06 15:46:44.485 20787-20787/com.ssa.digitaldeskclock D/IabHelper﹕ Starting in-app billing setup.
Problem was fixed by obtaining a new version of all of the util classes for the IAB v3 sample. I added all the new files to my util directory and it went flawlessly. Hopefully this can help someone else out there. See this link for the source
https://code.google.com/p/marketbilling/source/browse/v3/src/com/example/android/trivialdrivesample/util/IabHelper.java?r=5f6b7abfd0534acd5bfc7c14436f4500c99e0358