How to join an active session running on a Chromcast device - android

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!

Related

IntentService killed by system

I have an app where i can submit a timestatus to a server.
To save battery I queue the statusitems in a SQLite Database and submit them periodically with a JobScheduler who starts an IntentService to the server.
The function to insert the items looks like this:
public synchronized void workThroughQueue() {
try {
for (QueueItem queueItem : getAllQueueItems()) {
try {
dao.insert(queueItem.getItem());
delete(queueItem);
} catch (Exception e) {
Log.e(TAG, "Queueitem konnte nicht verarbeitet werden: " + e.getMessage(), e);
}
}
} catch (Exception e) {
Log.e(TAG, "Queueverarbeitung nicht vollständig: " + e.getMessage(), e);
}
}
The service (kotlin):
class QueueService : IntentService(QueueService::class.java.name) {
override fun onHandleIntent(intent: Intent?) {
Log.d("QueueService", "Jobservice started")
TimerecordQueue().workThroughQueue()
DangerAllowanceQueue().workThroughQueue()
ProjektEndQueue().workThroughQueue()
PhotoUploadQueue().workThroughQueue()
}
}
My problem is, if the process gets killed by the system cause of low memory during dao.insert(queueItem.getItem()); it sometimes gets successfully submitted to the server but it doesn't get deleted of the queue.
So the next time the queue starts it gets submitted again.
How can i resolve this problem?
Well, in this case you can do one of the two things
In the service after insertion keep the insertion record locally(in your local database) as boolean flag as well. That will let you know for sure that your records were submitted successfully.
Second way is, when you are hitting a service, on server database before inserting record check that record is already exist. As I can see you are sending item. There should be some sort of item id. Check on server database if that record against item id exists if yes leave that record.
This should do the trick.

How to call an Android app's method remotely?

I'm working on a project that improves Automation Test for Android's App. What I want to do is very "easy": I have this very simple SIP Client with a basic UI and developed just reading the API guides on the android developer website (https://developer.android.com/guide/topics/connectivity/sip.html) that receives and makes SIP calls.
I need to control remotely this app from my PC, connected at the same local network or the same wifi, by sending commands or similar (without interact with the phone) to the app itslef running normally on my phone.For a specific example I posted the method initiateCall() that calls sipAddress(in the app, sipAddress is taken from a Text Box), what I want to do is:
Starting the app on my phone
calling the method initiateCall() from my pc giving a sipAddress as a parameter (I must not use the UI from the app running, that's why I need to give the sipAddress)
check if an outgoing call starts from the app running on my phone
I thought that the solution must be something about web-services,but I don't have any better ideas and i don't know how to start and where to start solving this problem,that's why i need you help.
public void initiateCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// set up the listener for outgoing calls
#Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
updateStatus(call, 2);
}
#Override
public void onCallEnded(SipAudioCall call) {
updateStatus("Call End");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress,
listener, 30);
} catch (Exception e) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
You could do it REST API style. You would need to set up a minimalistic webserver.
If you access for example the url phoneip/ctrl/makecall?number=yournumber a serverside method us called if set up correctly. Then you can call you method and use the GET or POST variables as arguments.
You would have to look into Java Webserver Libraries/Frameworks. You can pick a lightweight one for that purpose. For example this one.
You could then also add security features (authentification to protect it) quite easily.
Example with sparkjava
import static spark.Spark.*;
....
get("/ctrl/makecall", (request, response) -> {
String phonenum = request.queryParams("number"); //may not be accurate; you have to determine the GET variable called "number" in that case; you can rename it; see docs!!!
//call your method with proper arguments
});

Can't able to receive group chat messages using smack-android:4.1.4

I am developing chat app using smack library. I have an issue in group chat. In my app, i am creating a group and in that members are auto-joined.i want to notify all user when I send a message in the group even if they had not initiated a chat.My code is as follow in that I have place listener in init method but unable to receive a message.
multiUserChatManager = MultiUserChatManager.getInstanceFor(mConnection);
mMultiUserChat = multiUserChatManager.getMultiUserChat(to);
mConnection.addAsyncStanzaListener(this, null);
DiscussionHistory history = new DiscussionHistory();
history.setMaxStanzas(0);
mMultiUserChat.addMessageListener(this);
mConnection.addSyncStanzaListener(this, null);
try {
mMultiUserChat.join(from, "", history, SmackConfiguration.getDefaultPacketReplyTimeout());
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
} catch (XMPPException.XMPPErrorException e) {
e.printStackTrace();
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
Here is message listener of group
#Override
public void processMessage(Message message) {
Logg.e(TAG,"Message received group..");
}
I don't know why this method does not call when someone send message in group, even I joined group, If I create 1 group and joined 2 users, when 1 user sends message in group then user2 can't able to receive message, but when user 2 send message inside this group then they both are able to receive messages.
Please help me, I can't able to find the solution. Please don't give suggestion which is already deprecated.
Thanks in Advance.!!
I'm full editing answer after full code review. -again-
I suggest to refactor your code to keep separation of roles in more than 1 huge class.
Basically you are especting messages in wrong listener due to many "addasync - addsync" in your code and you are able to receive messages just as side effect of your monster-class-all-in!
I see many optimization you need to apply to your code.
It's too long to explain and out of the question, however, just as example:
1. sendGroupMessage You can check by MultiUserChatManager if you
already joined the chat and then send the message. You must fire a
"join" just once, not everytime you want to send a message.
2. mMultiUserChat.addMessageListener(this);
A listener must be added ONCE or you'll create tons of threads. Probably it works because you have a singleton. While you have a listener, you don't need to add it anymore to that chat if you don't remove it.
mConnection.addSyncStanzaListener(this, null);
Be carefull: you are adding your listener (wich one? You implements tons of listeners with same class) to your connection. Before or later your code will eat an important stanza (prolly a custom IQ) and you'll have an hard to discovery side effects.
mConnection.addAsyncStanzaListener(this, null); same of 3
Check for ProviderManager.addExtensionProvider(), before or later
you'll need some.
Hope that helps.
Try This
step1 : 1 remove this
mConnection.addAsyncStanzaListener(this, null);
mConnection.addSyncStanzaListener(this, null);
Step 2 : add this
private StanzaTypeFilter serverFilter;
private StanzaListener stanzaListener = null;
private XMPPTCPConnection mConnection;
registerStanzaListener(); // where you init connection
public void registerStanzaListener() {
serverFilter = new StanzaTypeFilter(Message.class);
if (stanzaListener != null) {
mConnection.removeAsyncStanzaListener(stanzaListener);
}
stanzaListener = new StanzaListener() {
#Override
public void processPacket(Stanza packet) throws SmackException.NotConnectedException {
processMessage((Message) packet);
}
};
mConnection.addAsyncStanzaListener(stanzaListener, serverFilter);
}
}

Chromecast Android Sender RemoteMediaPlayer producing No current media session

I have been able to successfully cast video to a Chromecast and have the option let the video play when disconnecting and it all works great. However, if I choose to quit the application and let the video continue playing and then try to re-join the currently playing session and try to use the RemoteMediaPlayer to control the video I am getting: "java.lang.IllegalStateException: No current media session".
Just as a background, I am saving the route id and session id on the initial connect into preferences and am able to successfully call "Cast.CastApi.joinApplication" and when in the onResult I am recreating the Media Channel and setting the setMessageReceivedCallbacks like so:
Cast.CastApi.joinApplication(mApiClient,"xxxxxxxx",persistedSessionId).setResultCallback(new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(Cast.ApplicationConnectionResult applicationConnectionResult) {
Status status = applicationConnectionResult.getStatus();
if (status.isSuccess()) {
mRemoteMediaPlayer = new RemoteMediaPlayer();
mRemoteMediaPlayer.setOnStatusUpdatedListener(
new RemoteMediaPlayer.OnStatusUpdatedListener() {
#Override
public void onStatusUpdated() {
Log.d("----Chromecast----", "in onStatusUpdated");
}
});
mRemoteMediaPlayer.setOnMetadataUpdatedListener(
new RemoteMediaPlayer.OnMetadataUpdatedListener() {
#Override
public void onMetadataUpdated() {
Log.d("----Chromecast----", "in onMetadataUpdated");
}
});
try {
Cast.CastApi.setMessageReceivedCallbacks(mApiClient,mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
} catch (IOException e) {
Log.e("----Chromecast----", "Exception while creating media channel", e);
}
//-----------RESOLUTION START EDIT------------------
mRemoteMediaPlayer.requestStatus(mApiClient).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
Status stat = mediaChannelResult.getStatus();
if(stat.isSuccess()){
Log.d("----Chromecast----", "mMediaPlayer getMediaStatus success");
// Enable controls
}else{
Log.d("----Chromecast----", "mMediaPlayer getMediaStatus failure");
// Disable controls and handle failure
}
}
});
//-----------RESOLUTION END EDIT------------------
}else{
Log.d("----Chromecast----", "in status failed");
}
}
}
If I declare the RemoteMediaPlayer as static:
private static RemoteMediaPlayer mRemoteMediaPlayer;
I can join the existing session as well as control the media using commands like:
mRemoteMediaPlayer.play(mApiClient);
or
mRemoteMediaPlayer.pause(mApiClient);
But once I quit the application obviously the static object is destroyed and the app produces the aforementioned "No current media session" exception. I am definitely missing something because after I join the session and register the callback perhaps I need to start the session just like it was creating when I initially loaded the media using mRemoteMediaPlayer.load(.
Can someone please help as this is very frustrating?
The media session ID is part of the internal state of the RemoteMediaPlayer object. Whenever the receiver state changes, it sends updated state information to the sender, which then causes the internal state of the RemoteMediaPlayer object to get updated.
If you disconnect from the application, then this state inside the RemoteMediaPlayer will be cleared.
When you re-establish the connection to the (still running) receiver application, you need to call RemoteMediaPlayer.requestStatus() and wait for the OnStatusUpdatedListener.onStatusUpdated() callback. This will fetch the current media status (including the current session ID) from the receiver and update the internal state of the RemoteMediaPlayer object accordingly. Once this is done, if RemoteMediaPlayer.getMediaStatus() returns non-null, then it means that there is an active media session that you can control.
As user3408864 pointed out, requestStatus() after rejoining the session works. Here is how i managed to solve it in my case and it should work in yours.
if(MAIN_ACTIVITY.isConnected()){
if(MAIN_ACTIVITY.mRemoteMediaPlayer == null){
MAIN_ACTIVITY.setRemoteMediaPlayer();
}
MAIN_ACTIVITY.mRemoteMediaPlayer.requestStatus(MAIN_ACTIVITY.mApiClient).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
if(playToggle ==0){
try {
MAIN_ACTIVITY.mRemoteMediaPlayer.pause(MAIN_ACTIVITY.mApiClient);
playToggle =1;
} catch (IOException e) {
e.printStackTrace();
}
}else{
try {
MAIN_ACTIVITY.mRemoteMediaPlayer.play(MAIN_ACTIVITY.mApiClient);
playToggle =0;
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
Ignore, MAIN_ACTIVITY, it is just a static reference to my activity since i run this piece of code from a Service. Also, setRemoteMediaPlayer() is a method where i create a new RemoteMediaPlayer() and attach the corresponding Listeners.
Hopefully this helps. Also, sorry if any mistake, it is my first post to StackOverFlow.

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();
}

Categories

Resources