I'm having trouble to check if a participant is still connected to a room.
I wrote this:
bool status=GooglePlayGames.Native.PInvoke.MultiplayerParticipant.AutomatchingSentinel().IsConnectedToRoom();
if (status = true)
{
this.gameObject.GetComponent<Text> ().text = "participant is connected";
}
if (status = false)
{
this.gameObject.GetComponent<Text> ().text = "participant left";
}`
When players are connected, "participant is connected" is displayed.
But when a player press the home button, and go back to the screen, the participant had OnPeersDisconnected called and leave the room. But for the actual player, "participant is connected" is still displayed.
How can i know when the IsConnectedToRoom() is false?
This may still depend on how implementation is going to be. According to the document:
Make sure to construct your game logic carefully to take each participant's status and connectedness into account. For example, to determine if all racers have crossed the finish line, your game should only consider the participants who are connected; some may have left the room or never have accepted the invitation.
Detecting when a player is disconnected
Your player might be disconnected from the room due to network connectivity or server issues. To be notified when a player is disconnected from the room, implement the OnConnectedSetChanged method.
virtual void OnConnectedSetChanged(RealTimeRoom const &room) {
// Check the room's participants to see who connected/disconnected.
}
Hope this helps.
Related
I am developing an application for my friend who is in sales, this application will make phone calls one after another, as soon as one phone call gets disconnected, it will automatically make call to another number from the list. This list can be read from and xml data source or json or mongodb or even from excel sheet.
This could be an ios app that reads data from an end point and stores them and can initiate the call at any point and it wont stop until all the calls are made.
Next call will be made only after the first call has been finished.
I am thinking about using node based web app using google voice to trigger the chain.
I've no experience with ios / android apis but Im willing to work on that if it's a viable thing on that platform.
Note: what we're trying to avoid is whole process of
looking up the phone number.
touch hangup and then click for another phone number.
It should self trigger the next call as soon as current call gets disconnected.
Also we're trying to avoid any paid services like twillo.
Thanks in advance :)
for IOS, you could use CTCallCenter
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall *call){
if ([call.callState isEqualToString: CTCallStateConnected])
{
//NSLog(#"call stopped");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
//NSLog(#"call played");
}
else if ([call.callState isEqualToString: CTCallStateIncoming])
{
}
};
Download phone list, loop inside phone list, make a call, listening for CTCallCenter and appdelegate's Event, detect user have finish last call, our app active again, then make the next call.
Or you can try in Demo here !
I have an important question: I manage stream flow on my receiver but I want to use stop message from my sender for stop the stream. Currently I'm using this sender (https://github.com/googlecast/CastVideos-android) and this receiver (https://github.com/googlecast/CastReferencePlayer) but the problem is that the sender sends me a command to pause and not a stop during the live.
In the documentation, side sender Android, I'm reading that the behavior is correct and the receiver must send a media status update message back to the sender and should report the state as MediaStatus.PLAYER_STATE_IDLE with reason MediaStatus.IDLE_REASON_CANCELLED.
Arrived at this point I want to modify the sample receiver and manage the pause like a stop and not like a pause. I want to manage the stop because when the sender click the stop button the receiver must stop the video and the sender must destroy "every player" (player, miniplayer, etc).
So My idea is this:
sampleplayer.CastPlayer.prototype.onPause_ = function() {
this.log_('onPause');
this.cancelDeferredPlay_('media is paused');
var isIdle = this.state_ === sampleplayer.State.IDLE;
var isDone = this.mediaElement_.currentTime === this.mediaElement_.duration;
var isUnderflow = this.player_ && this.player_.getState()['underflow'];
if (isUnderflow) {
this.log_('isUnderflow');
this.setState_(sampleplayer.State.BUFFERING, false);
this.mediaManager_.broadcastStatus(/* includeMedia */ false);
} else if (!isIdle && !isDone) {
this.setState_(sampleplayer.State.PAUSED, false);
} else if(this.isLiveStream) {
this.log_('onStop');
this.cancelDeferredPlay_('media is stopped');
var self = this;
sampleplayer.transition_(self.element_, sampleplayer.TRANSITION_DURATION_,
function() {
self.setState_(sampleplayer.State.IDLE, false);
self.resetMediaElement_();
self.mediaManager_.setIdleReason("CANCELLED");
});
return ;
}
this.updateProgress_();
};
As you can see in the third branc, I control a variable that I saved during the load of the video and after I set the state of player to IDLE, reset the mediaElement and finally I send to broadcast the State = IDLE and Reason = CANCELLED. In this way the sender see the message because I look the log but doesn't interpret this.
Now I don't know how to continue and manage this behaviour. Advice are welcome. Thank you.
Just to make sure my answer is covering your question, let me simplify your questions as the following: when playing a live stream, you want to still see the "stop" button on the sender side but if the user taps on that button, you want to stop the playback and unload the media.
If this is correct, you can achieve that from the sender side as well: CastVideos-android uses CastCompanionLibrary. There are two places that you need to update in that library: in VideoCastManager#togglePlayback() and VideoCastControllerFragment#togglePlayback(). In the former place, you need to update the conditional there to read like the following:
if (isPlaying && isRemoteStreamLive()) {
stop();
} else if (isPlaying) {
pause();
} else {
... //leave as is
}
In the latter case, you need to update one of the switch statements:
case MediaStatus.PLAYER_STATE_PLAYING:
if (mSelectedMedia.getStreamType() == MediaInfo.STREAM_TYPE_LIVE) {
mCastManager.stop();
mPlaybackState = MediaStatus.PLAYER_STATE_IDLE;
} else {
mCastManager.pause();
mPlaybackState = MediaStatus.PLAYER_STATE_BUFFERING;
}
break;
Basically, you are adding a logic that if the content is playing remotely, "toggling playback" should call stop() rather than pause() when dealing with a live stream.
Note that calling stop() means you are completely unloading your media and you may need to do some additional work to play another media but I haven't tested that since I don't call stop() in the CastVideos app. Also note that I have not tested/tried the above suggestion since I don't have a live stream to play with but I am hoping it would work.
I am developing a turn based multiplayer game in Android using Google Play Game Services.
I have successfully ended the game when the user clicks the Finish Button:
Games.TurnBasedMultiplayer.finishMatch(mGoogleApiClient, mMatch.getMatchId(), mMatch.getData(), creatorResult, opponentResult)
.setResultCallback(new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
The current player's game gets updated in the "Completed Games" section.
However the opponent's game is listed as "My Turn"
From the Developing a Turn Based Multiplayer Game in Android page:
"Play Game services sends a notification to all other match participants to inform them that the match is over. These participants see this match under Your Turn category in their respective match list UIs. At this point, your game can call finishMatch() for these participants to save their final game data. Invoking this method also moves the match to the Completed Matches category in the participant’s match list UI."
How do I call finishMatch for the other players?
--Is it through the mGoogleApiClient
--Or is there a way to make the oppononent's match status = MATCH_STATUS_COMPLETE
Can someone please help?
You should make the following call on each of the other players devices:
Games.TurnBasedMultiplayer.finishMatch( mGoogleApiClient, mMatch.getMatchId());
This will cause the following status changes on each players device:
Match status will change from MATCH_STATUS_ACTIVE to
MATCH_STATUS_COMPLETE
Match turn Status will change from
MATCH_TURN_STATUS_MY_TURN to MATCH_TURN_STATUS_COMPLETE.
The participant status of the player will change from STATUS_JOINED to
STATUS_FINISHED.
Note that a players participant status may remain STATUS_JOINED on the other players devices (at least thats what I see in my implementation).
I'm working on an Android app that supports sending music to a ChromeCast. We'd like users to be able to cast entire music playlists while the app runs in the background.
When my Nexus 7 is not connected to USB power and I turn the screen inactivity timeout to 15 seconds in the settings, the app will disconnect from the ChromeCast about 90 seconds after the device powers off its screen.
I've identified that I'm getting a MediaRouter.Callback call to onRouteUnselected, and since that's the callback I get when a user disconnects from a route, I'm handling it by tearing down the ApplicationSession.
When I plug back in and check the logcat, I see this message around the same time:
I/MediaRouter(19970): Choosing a new selected route because the current one is no longer selectable: MediaRouter.RouteInfo{ uniqueId=... }
Can I do anything to avoid the route being unselected when the app is in the background, or is there something else I can do to get the behavior I want?
I eventually got around this by refusing to disconnect the message streams and tear down the session when the route was disconnected under these conditions, and silently re-select the route when it became available again. The route gets deselected, but it does not affect my casting session.
To do this, I check to see if the route exists when it's unselected.
public void onRouteUnselected(final MediaRouter router, final RouteInfo route) {
if (!onUiThread()) {
new Handler(Looper.getMainLooper()).post((new Runnable() {
#Override
public void run() {
onRouteUnselected(router, route);
}
}));
return;
}
boolean isThisRouteAvailable = doesRouterContainRoute(router, route);
mRouteToReconnectTo = null;
if (isThisRouteAvailable) {
// Perform code to close the message streams and tear down the session.
} else {
// The route was unselected because it's no longer available from the router,
// so try to just keep playing until the message streams get disconnected.
mRouteToReconnectTo = route;
// Short-circuited a disconnect.
}
}
Later, when the route comes back, we can immediately re-select it.
#Override
public void onRouteAdded(MediaRouter router, RouteInfo route) {
super.onRouteAdded(router, route);
// if mRouteToReconnectTo is not null, check to see if this route
// matches it, and reconnect if it does with router.selectRoute(route)
}
#Override
public void onRouteSelected(final MediaRouter router, final RouteInfo route) {
if (!onUiThread()) {
new Handler(Looper.getMainLooper()).post((new Runnable() {
#Override
public void run() {
onRouteSelected(router, route);
}
}));
return;
}
if (areRoutesEqual(mRouteToReconnectTo, route)) {
// Short-circuited a reconnect.
mRouteToReconnectTo = null;
return;
}
mRouteToReconnectTo = null;
// Standard post-selection stuff goes here
}
There's no good way to compare two RouteInfo's, so I ended up writing a helper function that compared their description strings.
Rooster's answer is perfectly feasible and actually provides good insight as to how to re-connect to a route once it comes back online....
but....just to give further insight on what's going on....
You're getting...
I/MediaRouter(19970): Choosing a new selected route because the current one is no longer selectable: MediaRouter.RouteInfo{ uniqueId=... }
because when the device goes to sleep and is NOT plugged into a power source, the WIFI hardware is going into a low-power profile mode (and possibly shutting down entirely). This results in packet loss and subsequently causes the MedaRouter to fire the onRouteUnselected callback.
To prevent the Wifi from turning off you could set a WakeLock on the Wifi in the following manner:
WifiLock wifiLock;
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF , "MyWifiLock");
wifiLock.acquire();
Using the flag WifiManager.WIFI_MODE_FULL_HIGH_PERF will keep the WIFI hardware alive and active when the device goes to sleep. Caution, this flag is only available to API 12 and above.
I tried using the WifiManager.WIFI_MODE_FULL flag when creating the WifiLock, but that didn't seem to do the trick.
Obviously anyone using any type of WifiLock or WakeLock should take considerable care in making sure locks released when no longer needed. Also, beware this will cause battery drain when the device screen is off.
If you used the sample code (Android in this case), you're probably doing this...
mSession.setStopApplicationWhenEnding(true);
mSession.endSession();
...when the route is unselected. If you instead do this...
mSession.setStopApplicationWhenEnding(false);
mSession.endSession();
...then you can clean up the session, but the Chromecast will keep the application alive. When the route becomes available again (or possibly when the user picks the device again) you can build a new session. I have yet to explore how to determine if the new session is talking to a "brand new" instance of the application or to the application left running from another session, but I'll update this answer when I do.
Ok so I have been trying to fix this for days, and I'm not coming here looking for someone to do my work for me as I have been troubleshooting and fixed every single error message in the LogCat. I am developing an Android game using Andengine (this might be part of the problem so being familiar with it could help). I'm not doing anything too fancy, my game activities are all single scene and don't have any Physics or anything like that, just a bunch of sprites and textures. I also used Andengine for all of the other activities in my game because I find it to be a very easy way to set up graphically appealing screens. One such screen is my in-app store, where users can buy levelpacks and new sprites. The billing part of this all works great, the purchases go through to the Market and there's nothing too complicated there...
When the user clicks buy, the market screen pops up and loads the product they have selected (these are real products, not the android tests although the game is not published). The Market screen pops up over the current activity, regardless of whether I use the "Android 2.0" implementation where it is part of the game's stack or I use the "Android 1.6" implementation and it is part of its own stack. I would prefer to use the Android 2.0 implementation but if I can only get the 1.6 to work I will take that. So anyway, the problem arises when the user either cancels the purchase using the back button or completes the purchase with a credit card, both result in the market screen disappearing and the app starting a new activity that is just a black screen (which eventually times out and causes a force close). The purchase goes through OK but the user does not get the product because the game force quits before we get to the code to change the user's items in the game. Now for some code, I used this tutorial (http://www.anddev.org/advanced-tutorials-f21/simple-inapp-billing-payment-t52060.html) without changing much of anything. The BillingHelper class is most important, as it holds the requestPurchase() method and the startBuyPageActivity() methods. I call request purchase from my StoreFront activity like this:
BillingHelper.requestPurchase(StoreFront.this, itemID);
and in the onCreate of the StoreFront I have this stuff (as told to do by the tut):
startService(new Intent(mContext, BillingService.class));
BillingHelper.setCompletedHandler(mTransactionHandler);
...
//some handler that billing needs
public Handler mTransactionHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
Log.i(TAG, "Transaction complete");
Log.i(TAG, "Transaction status: "+BillingHelper.latestPurchase.purchaseState);
Log.i(TAG, "Item purchased is: "+BillingHelper.latestPurchase.productId);
if(BillingHelper.latestPurchase.isPurchased()){
//TODO do something here if we've completed our latest purchase,
//this should be with the status bar notifications and
//saved preferences
}
};
};
So I don't think the problem lies there. Here are the relevant parts of BillingHelper
protected static void requestPurchase(Context activityContext, String itemId){
if (amIDead()) {
return;
}
Log.i(TAG, "requestPurchase()");
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString("ITEM_ID", itemId);
try {
Bundle response = mService.sendBillingRequest(request);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
//The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI
PendingIntent pendingIntent = (PendingIntent) response.get("PURCHASE_INTENT");
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "REQUEST_PURCHASE Sync Response code: "+responseCode.toString());
startBuyPageActivity(pendingIntent, new Intent(), activityContext);
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: "+isBillingSupported());
}
}
Which I have tried calling from StoreFront with a variety of arguments as "ActivityContext" such as StoreFront.this, getApplicationContext(), a static context store elsewhere, a static Activity stored elsewhere, getBaseContext() anything I could possible think of...
Here is the other relevant activity
private static void startBuyPageActivity(PendingIntent pendingIntent, Intent intent, Context context){
//android 1.6 method
try {
pendingIntent.send(context, 0, intent);
} catch (CanceledException e){
Log.e(TAG, "startBuyPageActivity CanceledException");
}
}
Nothing fancy, I just want the user to be returned to any of my various activities (preferably StoreFront) when they either buy the item or press back during the process. HELP PLEASE!
Edit: I want any possible solution to allow in-app billing to return to my app after the purchase is complete, even the messiest solution.
EDIT
A logcat and method calls of what the issue:
"BillingService Starting",
BillingHelper.setCompletedHandler(),
StoreFront.onStart() called,
StoreFront.onResume() called,
"BillingService Service starting with onCreate",
"BillingService Market Billing Service Successfully Bound",
"BillingService Market Billing Service Connected",
BillingHelper.instantiateHelper(),
then this is where I actually click the buy button in the store (all of that runs just when opening StoreFront):
BillingHelper.setCompletedHandler(),
BillingHelper.isBillingSupported(),
BillingHelper.amIDead(),
BillingHelper.makeRequestBundle(),
"BillingService isBillingSupported response was: RESULT OK",
BillingHelper.requestPurchase(),
BillingHelper.amIDead(),
"BillingService requestPurchase()",
BillingHelper.makeRequestBundle(),
"BillingService current request is ......",
"BillingService REQUEST PURCHASE Sync Response code: RESULT OK",
BillingHelper.startBuyPageActivity(),
"BillingService Recieved action: com.android.vending.billing.RESPONSE CODE",
"BillingService checkResponseCode got requestID..."
"BillingService checkResponseCode go responseCode RESULT ERROR"
(this is because I can't purchase on this device),
and then I get an Error message saying: "E 32427 Surface surface (identity=5925) is invalid, err=-19 (No such device)" and from there nothing works anymore.
Also I have tested this on a different phone (another developer I am working with, who can actually buy things in it but still gets the black screen error) and he never got the Handler messages you mentioned in your comment either
Edit: if I had to guess where the error is, I'd say it's this
06-16 11:20:23.635: DEBUG/dalvikvm(3807): GC_EXPLICIT freed 53K, 45% free 3710K/6663K, external 1K/513K, paused 102ms
06-16 11:20:23.885: ERROR/Surface(3807): surface (identity=158) is invalid, err=-19 (No such device)
06-16 11:20:23.905: ERROR/Surface(3807): surface (identity=158) is invalid, err=-19 (No such device)
06-16 11:20:23.905: ERROR/Surface(3807): surface (identity=158) is invalid, err=-19 (No such device)
06-16 11:20:23.905: ERROR/Adreno200-EGL(3807): egliSwapWindowSurface: unable to dequeue native buffer
Note that the interrupted exception is expected by the Andengine library so that is a red herring.
Also (I hope this is allowed on here) I will offer paypal reward for a solution. If this is against the Terms of SO then just delete this line, please don't close this question.
I may know what's wrong and I have a test for you to do. The buy screen runs a finish call after the user cancels the purchase or completes the purchase. For me for some reason the finish call was drifting down into the currently running activity and (CLOSING??? it).
Here's the relevant line from the log:
06-16 11:20:22.774: WARN/ActivityManager(132): Duplicate finish request for HistoryRecord{40ace828 com.android.vending/.billing.InAppBuyPageActivity}
I think I fixed this in my code problem is that I can't remember exactly what I did (maybe didn't call finish in my purchase complete handler...)
I don't know anything about Andgen, but what would happen if a finish call got called on the main Andgen activity? I'd imagine it would stop execution and you might get a black screen and an app crash.
So to test this, create a separate activity for you buy page. Doesn't need to be complicated - maybe have it just buy one canned product after it starts up. Run your code and see if it still gives you the black screen of doom. My bet: it may exit out of the activity back to your game but I think it'll work.
Hope this helps and Good Luck!