In App Billing foreground - android

I have inAppBilling working but i'm trying to refine it a little bit. I noticed that when I try in app billing in Angry Birds it opens directly on top of the application but my application pulls in app billing to the foreground/desktop. I read the docs and it states that singleTop must be off and reflection must be used as well with an activity context given and NOT an application context. I have verified that I have all of those things done but its still pulling to the foreground. Any ideas?
My verification:
I have an activity called MyActivity
Log.d("TEST", mContext.getClass().getName());
responds back with MyActivity
and in Android Manifest for MyActivity
android:launchMode="standard"
EDIT:
The code that starts the checkout activity
void startBuyPageActivity(PendingIntent pendingIntent, Intent intent) {
if (mStartIntentSender != null) {
// This is on Android 2.0 and beyond. The in-app buy page activity
// must be on the activity stack of the application.
try {
// This implements the method call:
// mActivity.startIntentSender(pendingIntent.getIntentSender(),
// intent, 0, 0, 0);
mStartIntentSenderArgs[0] = pendingIntent.getIntentSender();
mStartIntentSenderArgs[1] = intent;
mStartIntentSenderArgs[2] = Integer.valueOf(0);
mStartIntentSenderArgs[3] = Integer.valueOf(0);
mStartIntentSenderArgs[4] = Integer.valueOf(0);
mStartIntentSender.invoke(mActivity, mStartIntentSenderArgs);
Log.d("TAG", mActivity.getClass().getName());
} catch (Exception e) {
Log.e(TAG, "error starting activity", e);
}
} else {
// This is on Android version 1.6. The in-app buy page activity must be on its
// own separate activity stack instead of on the activity stack of
// the application.
try {
pendingIntent.send(mActivity, 0 /* code */, intent);
} catch (CanceledException e) {
Log.e(TAG, "error starting activity", e);
}
}
}
What I am trying to accomplish is for the in app billing prompt whether it finishes the transaction or cancels to resume back to my game instead of simply closing and finishing. A current fix I have is to start the games Activity again which would result in reloading everything and putting the player back at the main menu page.

Related

Force an activity to remain in memory

I have implemented in app billing in a game. We are using unity3d as our game development engine . For in app billing to work I wrote a java native plugin that handles in app billing jobs and send messages back to unity application when purchase is done.
The way I implemented this is to start another activity to handle billing tasks and finish that activity after the tasks are done to return to unity app. But the problem is that in some devices(specially with low RAM) the unity app get closed when the billing activity is started . So when the user purchase an item our listeners in unity app are no longer valid and the unity app is restarted. We can't give them the item which is bought (It's consumed).
So my question is that is there any way to force the unity app remain untouched while the billing activity is running ?
My Code that fires billing activity:
public static void Buy(String SKU,String developerPayLoad,boolean Consumable)
{
Intent buyIntent = new Intent(gameActivity,MyketActivity.class);
buyIntent.putExtra("OPERATION", "PURCHASE");
buyIntent.putExtra("SKU", SKU);
buyIntent.putExtra("PayLoad", developerPayLoad);
buyIntent.putExtra("Consumable", Consumable);
gameActivity.startActivityForResult(buyIntent, 8586);
}
//The billing activity's OnCreate
protected void onCreate(Bundle bundle)
{
String Operation = getIntent().getStringExtra("OPERATION");
super.onCreate(bundle);
if(!FirstTime)
{
MyketHelper.Log("Not for the First Time");
finish();
return;
}
if(Operation.equals("PURCHASE"))
{
if(MyketHelper.helper==null)
{
finish();
return;
}
MyketHelper.Log("Purchase Flow Launched");
SKU = getIntent().getStringExtra("SKU");
PayLoad = getIntent().getStringExtra("PayLoad");
Consumeable = getIntent().getBooleanExtra("Consumable", true);
MyketHelper.helper.launchPurchaseFlow(this, SKU, 8585, OnPurchase);
}
}

Robotium testcase

I have a Robotium test case and It should be like
UI Application starts uploading data to server
User swaps to some other application on the device
uploading operation is running at the background
user comes to the main UI application
How to keep track of uploading the data at background? can we use multithreading for this?
try {
mSolo.clickOnMenuItem("UPLOAD");
mSolo.sleep(1000);
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
mSolo.waitForActivity(Settings.ACTION_APPLICATION_SETTINGS);
mSolo.goBack();
mSolo.assertCurrentActivity("main",
UIActivity.class);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Is this code correct? If not suggest me a modification or correct code.
Help is always appreciated,
Thanks
You cannot interact with other applications unless you signed the third party application with your own key (see black box testing).
But what you can is pressing Home, Back and starting Intents. The following code is untested but hopefully gives you an idea:
try {
mSolo.clickOnMenuItem("UPLOAD"); // start upload
mSolo.sleep(1000);
mSolo.goBack(); // leave app
...
Intent intent = new Intent("com.company.another.app.SomeActivity");
startActivity(inent); // start another app
...
// option one: get app context and use it for access on preferences, etc.
Context context = this.getInstrumentation().getTargetContext().getApplicationContext();
// option two: wait for logs that you write while uploading
solo.waitForLogMessage("Upload completed");
...
Intent intent = new Intent("com.myapp.MyMainUIActivity");
startActivity(inent); // start own Main Activity again
...
} catch (Exception e) {
e.printStackTrace();
}
So you could use log messages, preferences or any other methods of your app in order to follow up the upload progress.
You cannot leave your application and run it again with Instrumentation. This part is not correct:
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
Why do you create new instrumentation? You can simply run:
getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
by the way, solo.goBack() just does it, so it doesn't make sense to call it with instrumentation. I would simply rewrite it to:
try {
mSolo.clickOnMenuItem("UPLOAD");
mSolo.sleep(1000);
mSolo.goBack();
assertTrue(mSolo.waitForActivity(Settings.ACTION_APPLICATION_SETTINGS));
mSolo.goBack();
mSolo.assertCurrentActivity("main", UIActivity.class);
} catch (Exception e) {
e.printStackTrace();
}

How to join an active session running on a Chromcast device

I have to Users (User A and B) and one Chromecast device (C1).
User B starts a stream on C1.
User A connects to C1
Now User A should be able to control the stream running on C1. But every time I want to start a session the running stream on C1 is shut down and the receiver app is restarting.
Is there a way to join an active session? Or is that a job which has to be done by the web app running on the Chromecast device?
EDIT:
my sender app is a native Android app
Thanks!
You should have a look to the TicTacToe application. I think it does exactly that where 2 players can join the same game :
https://github.com/googlecast/cast-android-tictactoe
Hope this helps.
JN
What sort of sender are you using? Is it a native app (i.e. using Android or iOs SDK on a mobile device) or the sender is a chrome app?
On the receiver, you create a Receiver object and a ChannelHandler. You use the receiver to generate a ChannelFactory which you then pass to the ChannelHandler. The ChannelHandler now handles the creation of channels on the receiver. You will want to add an EventListener to the handler to listen to messages. Based on those messages you can do various things.
receiver = new cast.receiver.Receiver(YOUR_APP_ID, [YOUR_PROTOCOL], "", 5);
var dashHandler = new cast.receiver.ChannelHandler(YOUR_PROTOCOL);
dashHandler.addChannelFactory(receiver.createChannelFactory(YOUR_PROTOCOL));
dashHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
receiver.start();
...
onMessage = function (e) {
var message = e.message;
switch (message.type) {
...
}
}
On the sender, after a session is created you will want to send a check status message to the receiver to see if there are already channels attached. You can do this via your MessageStream and your receiver needs to respond in such a way that the MessageStream gets its status updated. You check that status to see if there are channels. If there are you can start listening to updates for your receiver. If not you can send a load event to the receiver to start your activity.
MediaProtocolCommand cmd = mMessageStream.requestStatus();
cmd.setListener(new MediaProtocolCommand.Listener() {
#Override
public void onCompleted(MediaProtocolCommand mPCommand) {
if (mMessageStream.getState() == 'channelsExist') {
//Start New Activity
} else {
//Join Existing Activity
}
#Override
public void onCancelled(MediaProtocolCommand mPCommand) {
}
});
This is kind of a vague response, but it could be more specific if I knew what you were trying to do. My app is using Google's RAMP protocol to play videos so my MessageStream and all it's messages are already defined. If you're doing something different, you need to create your own MessageStream.
Sorry for the late answer, but I figured it out by myself: It wasn't such complicated at all
I started the an Application like this
try {
mSession.startSession(applicationName,applicationArgs);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
But it seems, that the MimeData applicationArgs is not needed at all. By removing the arguments and starting the session like below it works really fine!
try {
mSession.startSession(applicationName);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
I hope this works for you too!

Android navigation button up on Jelly Bean

I developed an application that contains a homescreen with an article list.
If you click on it, you access the detail in another screen.
I implemented the ActionBarSherlock, so I used the "up" button pattern for this activity.
Then I added a widget to this application. When you click on the widget, you access directly the detail activity.
The "up" button has been implemented following the Google recommandations (http://developer.android.com/training/implementing-navigation/ancestral.html).
My problem is that on API Level 15 and below, it works perfectly. It calls the following code :
#Override
public boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
String action = activity.getIntent().getAction();
return action != null && !action.equals(Intent.ACTION_MAIN);
}
But on JellyBean, the code used is :
public boolean shouldUpRecreateTask(Intent targetIntent) {
try {
PackageManager pm = getPackageManager();
ComponentName cn = targetIntent.getComponent();
if (cn == null) {
cn = targetIntent.resolveActivity(pm);
}
ActivityInfo info = pm.getActivityInfo(cn, 0);
if (info.taskAffinity == null) {
return false;
}
return !ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
} catch (RemoteException e) {
return false;
} catch (NameNotFoundException e) {
return false;
}
}
The first part of the method retrieves information on the activity that should be loaded if stack must be recreated.
But I still don't understand what does the line :
!ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
Can anyone help me on this line, I really need to find out how to obtain true by initializing everything well ?
Its a boolean method it has to return something. If it needs to return a true boolean variable for it work, you have to do so!
From the official Documentation:
Returns true if the app should recreate the task when navigating 'up'
from this activity by using targetIntent.
If this method returns false the app can trivially call navigateUpTo(Intent)
using the same parameters to correctly perform up navigation.
If this method returns false, the app should synthesize a new task stack by using
TaskStackBuilder or another similar mechanism to perform up navigation.
The affinity indicates which task an activity prefers to belong to. By default, all the activities from the same application have an affinity for each other. So, by default, all activities in the same application prefer to be in the same task. However, you can modify the default affinity for an activity. Activities defined in different applications can share an affinity, or activities defined in the same application can be assigned different task affinities.

startActivity to relaunch HomeActivity from another activity in the same application doesn't work in Gingerbread

In my application I have used some code from the iosched 2012 app. In specific the starting workflow is the following:
1.The user presses the launcher icon of the app
2.HomeActivity checks if the user is authenticated. If he/she is not, it starts the Authentication activity, passing it intent to it and finishes itself
3.When the login process is successful, the authenction activity starts an activity in order to start the HomeActivity and finishes itself
4.HomeActivity checks again if the user is authenticated and displays the home screen of the application.
The following code works like a charm in API Level > 11. Today, I tried the app in a Gingerbread and it fails. Step 3 works, but although the HomeActivity starts it's not brought to front. You have to use the recent list and choose the application in order to see the homeactivity and its now displayed content.
Here's the code and check from the HomeActivity in the oncCreate method
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!AccountUtils.isSystemAuthenticated(this)) {
AccountUtils.startSystemAuthentication(this, getIntent());
finish();
} else if(!AccountUtils.isAppAuthenticated(this)) {
AccountUtils.startAppAuthentication(this, getIntent());
finish();
}
if(isFinishing()) {
return;
}
setContentView(R.layout.activity_main);
...
}
}
The method invoked in the Authentication activity after the login process is completed
protected void handleLoginSuccess(LoginServiceResponse response, String username, String password) {
if(....) {
if(mFinishIntent != null) {
mFinishIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mFinishIntent.setAction(Intent.ACTION_MAIN);
mFinishIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(mFinishIntent);
}
finish();
} else {
super.handleLoginSuccess(response, username, password);
}
}
Where the mFinishIntent member variable is the intent passed from the HomeActivity (using getIntent())
As I mentioned, in API Level > 11, this works well, and the breakpoint in HomeActivity's onCreted method is hit twice, while in a Gingerbread phone, is hit only once (only when the application starts).
Do I have to use another flag or do you have any other idea of what's going on?
Thanks
What is probably happening is that the activity is only created when the app is started and then when you go back to it from the Authentication activity, it is simply resumed. Try putting the authentication checking code in HomeActivity in the onResume() method.
Here is some more info: http://developer.android.com/reference/android/app/Activity.html#ProcessLifecycle

Categories

Resources