handling packet listeners in xmpp - android

I am trying to develop a android chat application using asmack api and server is openfire.
I am at the stage of handling incoming messages from users and representing them corresponding user chat screen (i.e. activity).
So for that, I developed 2 activities i.e.
public class ResultActivity extends Activity
public class UserActivity extends Activity
resultactivity is the one which maintains roster presence information like online , away etc. so this would be only one instance.
useractivity is the one which maintain chat list with the corresponding user. This activity starts with onclick event on list in ResultActivity. so this can be more than one based on no of users on Result activity..
So to listen to incoming packets (i.e. incoming messages) I added a listener in the ResultActivity which adds messages into global arraylist.
In Result Activity:
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
MainActivity.connection.addPacketListener(receive_message, filter);
PacketListener receive_message = new PacketListener() {
public void processPacket(Packet packet) {
message = (Message) packet;
String sender=null,body=null,sender_final=null;
if (message.getBody() !=null) {
Log.d("UA", "message from "+StringUtils.parseBareAddress(message.getFrom()));
Log.d("UA", "message is "+message.getBody());
sender = StringUtils.parseBareAddress(message.getFrom());
sender_final=sender.substring(0, sender.indexOf('#'));
body = message.getBody();
Log.d("UA", "Sender : "+sender_final+" body : "+body);
userMessage = new UserMessage("In", sender_final, body);
userMessage_list.add(userMessage);
userMessage_list.add(userMessage);
}
}
};
After adding the message in the global message list (i.e. userMessage_list) I need to update the user activity with incoming message. userMessage_list is the array list used as array in the getview method of base adapter extended class.
So Now I need the update the userActivity list with notifydatasetchanged method.
In UserActivity :
private class UserActivityThread extends Thread {
#Override
public void run() {
UserActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
userAdapter.notifyDataSetChanged();
}
});
}
}
So how to call the notifydatasetchanged method in the another activity.
I am curious to know how this scenario is handled in whatsapp or any chat applications.

I have solved this problem in my chat app by using a ContentProvider class and a CursorLoaderwhich will be automatically notified everytime certain URI has new records.
In your case I would recommend you launching a broadcast message. Your class which has a ListView with the messages will have registered a BroadcastReceiver and it will be listening to possible updates. Everytime your BroadcastReceiver.onReceive is triggered in your activity, refresh your adapter.
Hope it helps :)
EDIT
How to achieve this:
Create a BroadcastReceiver object in your activity.
BroadcastReceiver mReceiver;
Code your broadcast receiver (on your onCreate for example)
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//extract our message from intent
String extra= intent.getStringExtra("extra");
Log.i("msg received: ", extra);
}
};
Add IntentFilter (in your onResume could be valid)
IntentFilter intentFilter = new IntentFilter("com.myproject.myintentfilter");
4.1 register your receiver
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
registerReceiver(mReceiver,intentFilter);
}
4.2 Unregister your receiver
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
this.unregisterReceiver(mReceiver);
}
Make sure your receiver will receive the message, cheking that your sendBroadcast matches the IntentFilter.
Finally:
Intent i = new Intent("com.myproject.myintentfilter").putExtra("extra", "This is a new value");
this.sendBroadcast(i);
EDIT 2
In the case of receiving a message during your activity rotation (receiver is not registered) I have found 2 solutions which I am not very satisfied with, but they work.
Reload your listview after orientation change, as the activity destroys itself, you can just reload the information of your listview, your new message should appear anyway.
Put a static field in your activity, everytime you receive a message, put such variable to TRUE. If your broadcastReceiver triggers, put that variable to false, and when your onResume / onCreate method is called after a change of orientation, check your static variable and do your reload if the variable was true (put it to false after reloading the ListView).

Related

Launch app from wear and get the message regardless the state of app

I'm dealing with wearable, and my purpose is the next:
From my watch, I want to press a simple button, which send a simple message to the mobile. But I would like to handle all those behaviors :
when mobile app isn't yet launched, then launch the app and pass the message from wear, which can be handled in the launcher activity
when mobile app is launched but in the background, then just bring it to foreground and handle message from wear, which can be handled in the launcher activity
when mobile app is launched and in foreground, juste handle the message in the launcher activity
So far, I handle to launch the app when it isn't not yet launched, but I can't get the extra message in the launcher activity contained in the intent. Here the code.
the mobile service
public class MobileWearService extends WearableListenerService {
private static final String START_ACTIVITY = "/start_activity";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
String event = messageEvent.getPath();
String msg = new String(messageEvent.getData());
if (event.equals(START_ACTIVITY)) {
Intent intent = new Intent( this, MainActivity.class );
intent.putExtra("Data", msg);
intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity( intent );
}
}
}
However, if I use a broadcast to send the message from service to the main activity, it works only if the app is launched and foreground
public class MobileWearService extends WearableListenerService {
private static final String START_ACTIVITY = "/start_activity";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
String event = messageEvent.getPath();
String msg = new String(messageEvent.getData());
if (event.equals(START_ACTIVITY)) {
broadcastIntent.setAction("com.me.project.wear.to.app");
broadcastIntent.putExtra("Data", msg);
broadcastIntent.putExtras(intent);
sendBroadcast(broadcastIntent);
}
}
}
launcher activity
private IntentFilter mIntentFilter = new IntentFilter("com.me.project.wear.to.app");
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals("com.me.project.wear.to.app")) {
String msg = intent.getStringExtra("Data");
}
}
};
#Override
protected void onResume() {
super.onResume();
registerReceiver(mReceiver, mIntentFilter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
So I would to combine the fact to get the message from wear (I know how to) but pass this message to get it in the launcher activity regardless of the state of the app.
Just make the static BroadcastReceiver
public class WatchMessageReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null &&
intent.getAction().equals("com.me.project.wear.to.app")) {
String msg = intent.getStringExtra("Data");
Intent launcherIntent = new Intent(context, LauncherActivity.class);
launcherIntent.putExtra("Data",msg);
startActivity(launcherIntent);
}
}
}
in your manifest file
<receiver android:name ="WatchMessageReceiver"
<intent-filter>
<action android:name="com.me.project.wear.to.app"/>
</intent-filter>
</receiver>
In the Sending and Syncing Data training, there is a Handling Data Layer Events:
When you make a call to the Data Layer API, you can receive the status of the call when it completes. You also can listen for data events, resulting from data changes that your application makes anywhere on the Android Wear network.
Listen for Data Layer Events
Because the data layer synchronizes and sends data across the handheld and wearable, it is usually necessary to listen for important events. Examples of such events include creation of data items and receipt of messages.
To listen for data layer events, you have two options:
Create a service that extends WearableListenerService.
Create an activity that implements DataApi.DataListener.
With both these options, you override the data event callback methods for the events you are interested in handling.
Some of the events you can listen for using WearableListenerService are as follows:
onDataChanged(): Whenever a data item object is created, deleted, or changed, the system triggers this callback on all connected nodes.
onMessageReceived(): A message sent from a node triggers this callback on the target node.
onCapabilityChanged(): When a capability that an instance of your app advertises becomes available on the network, that event triggers this callback. If you're looking for a nearby node you can query the isNearby() method of the nodes provided in the callback.
According to the related SO post:
WearableListenerService does not run constantly - it is only started when a new message/node connection/data layer change is sent and stopped when there are no more messages.
Hope this helps.

dynamically update listview when a gcm push notification is received without showing it in the notification bar

i am having a group chat feature.every person in the group will receive the push(even the sender of the message).
if the person has opened the group i.e the chatting area is visible, then i want that the push does not show up in the notification bar and it directly update the chat (which i am showing in a listview ).
Initially the chat history i get from web service (when the user open the chat area)
Hope i am able to make you guys clear with what i want to achieve .thanks in advance
First of all, when you get message in onMessage() of GCMIntentService, send brodcast. Like,
Intent i = new Intent();
i.setAction("appendChatScreenMsg");
i.putExtra("sender_id", b.getString("sender_id"));
i.putExtra("message", b.getString("message"));
i.putExtra("time", getCurrentTime());
i.putExtra("date", getCurrentDate());
this.sendBroadcast(i);
Next, Make BroadcastReceiver in your Chat activity or Chat Fragment. Like,
BroadcastReceiver appendChatScreenMsgReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
if (b != null) {
int totalItems = adapter.getCount() - 1;
ChatModel model = new ChatModel("" + sharedPreferences.getString(VariableBag.USERID, ""), ""+ b.getString("sender_id"), "" + b.getString("message"), b.getString("date"), b.getString("time"));
arrChat.add(model);
if (adapter != null) {
if (lstChat.getLastVisiblePosition() == totalItems) {
adapter.notifyDataSetChanged();
lstChat.setSelection(adapter.getCount());
} else {
adapter.notifyDataSetChanged();
}
} else {
adapter = new ChatAdapter(getActivity());
lstChat.setAdapter(adapter);
adapter.notifyDataSetChanged();
lstChat.setSelection(adapter.getCount());
}
}
}
};
Next, Register BroadcastReceiver in onCreate(). Like
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().registerReceiver(this.appendChatScreenMsgReceiver, new IntentFilter("appendChatScreenMsg"));
}
Next, unregisterReceiver in onDestroy(). Like,
#Override
public void onDestroy() {
super.onDestroy();
getActivity().unregisterReceiver(appendChatScreenMsgReceiver);
}
Explanation :
1.) When message is received in GCMIntentService(), first of all, check weather
you are in chat screen or not.
2.) If you are in chat screen, broadcast your message using Intent and Broadcast.
3.) Now, Create your BroadcastReceiver() in chat screen.
4.) Register your BroadcastReceiver() in onCreate() and unregister in onDestroy().
5.) When message is broadcast and you are in chat screen, this broadcast receiver get your bundle.
6.) Now Whatever you want to do.
7.) If you are not in chat screen, then show respected message in notification. Don't broadcast.
Note: Be sure in which screen you are currently.
I think you should use Observer Design Pattern.
set a listener where you display messages and in your service where you get your messages from server check for listener, if listener wasnt null call a update method from listener and update your list else show notification.
example of this patern :
http://www.vogella.com/tutorials/DesignPatternObserver/article.html

onReceive called many times

I have a service that get data from an other application.
When I get date I send message to broadCast in order to refresh the UI.
The onReceive method is called many times and data displayed multiple times.
this is my code:
DataService.java
if(sizeLat == 1) {
sendMessage("Alerte1;");
}
else {
sendMessage("Alerte2;");
}
private void sendMessage(String message) {
Log.w("","==> send message");
Intent intent = new Intent("my-event");
// add data
intent.putExtra("message", message);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
MainActivity.java
// handler for received Intents for the "my-event" event
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("","Onreceiver");
String message = intent.getStringExtra("message");
if(message.equals("Alerte1")){
parentItems.add(message);
adapter.notifyDataSetChanged();
}}};
#Override
protected void onResume() {
Log.d(TAG, "On Resume");
super.onResume();
// Register mMessageReceiver to receive messages.
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event"));}
How can I resolve the problem ?
Put broadcast register line in onCreate and unregister it in onDestroy() method. The line which you have to move from onResume() to onCreate is:-
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event"));}
Possibly, you have two instances of the activity living at the same time. Make a breakpoint on the message receiver and check the address of the instance of your activity class and see if they are different each time the onReceive is called.
There are a few reasons why you could have two instances living at the same time, but one of the most common is leaking context within the activity.
More on this topic.
I fixed same problem by unregister BroadcastReceiver in onPause method
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver,new IntentFilter("my-event")));
Register it in OnResume Method
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event"));}

how to detect and notify user of message from server?

app sent transaction to the server, user closes app, now a message needs to be
sent back to the phone from the server 10+ minutes later. The phone may be asleep, or the user might be checking his email. The question which I have is:
how can the phone be notified that a message has been received from server ?
how to display that message ?
A possible solution would be Google cloud messaging, but I still am not able to answer these 2 questions
1) You have to use SERVICE for that.
2) And to show that message.
Do like this.
The variable and method are members of Service class:
public final static String ACTION = "com.apps.example.MainActivity";
private void messageFromServer()//this method sends broadcast messages
{
Intent intent = new Intent(MOVEMENT_UPDATE);
String messageFromServer=serverMessage //here you will put server message
intent.putExtra("MessageFromServer", messageFromServer);
sendBroadcast(intent);
}
And this are the methods from Main activity:
You have to register receiver in the onResume method:
#Override
public void onResume()
{
IntentFilter intentFilter;
intentFilter= new IntentFilter(YourService.ACTION);
messageFromServer= new MessageFromServer();
registerReceiver(messageFromServer, intentFilter);
startYourService();
super.onResume();
}
private void startYourService()
{
startService(new Intent(this, YourService.class));
}
public class MessageFromServer extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// this method receives broadcast messages.
// Be sure to modify AndroidManifest.xml file in
// order to enable message receiving
String messageFromServer = intent.getStringExtra("MessageFromServer");
updateGUI();// here you can update the ui
}
}
and put service in you manifest file.
<service android:name="com.apps.service.YourService" ></service>

sending broadcasts to activity having google map register/unregister best places?

My Application has a main screen containing a map ,it also contains several other screens And around 3 services sending broadcast updates to the map(polylines , markers ...).
the map updated by broadcasts from the services
for register/unregister of the broadcasts i have two options :
1-register/unregister broadcast on onPause/onResume
PROS :only when the map is visible it can receives updates
CONS:when the map goes to the background and there are several updates i will need to do them all onResume which can cause some slowness in the application
2-register/unregister broadcast on onCreate/onDestroy
PROS:the map is always up to date regardless of which screen you are in
CONS:i am not sure if this will cause memory problems or any other unforeseen issues
As for the fact that the onDestroy might not be called for sometime i already took that in to consideration , and designed the application in a way that if "the application" not in foreground anymore it will shutdown all the services after 30 sec.
what is the best way to handle this ?
The LocalBroadcastManager class is used to register for and send broadcasts of Intents to local objects within your process. This is faster and more secure as your events don't leave your application.
The following example shows an activity which registers for a customer event called my-event.
#Override
public void onResume() {
super.onResume();
// Register mMessageReceiver to receive messages.
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event"));
}
// handler for received Intents for the "my-event" event
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
String message = intent.getStringExtra("message");
Log.d("receiver", "Got message: " + message);
}
};
#Override
protected void onPause() {
// Unregister since the activity is not visible
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
}
// This method is assigned to button in the layout
// via the onClick property
public void onClick(View view) {
sendMessage();
}
// Send an Intent with an action named "my-event".
private void sendMessage() {
Intent intent = new Intent("my-event");
// add data
intent.putExtra("message", "data");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

Categories

Resources