5

I am building a chat application and I have got a question about my fragment containing a listview with all the chats.

When a user receives a message the chats list is updated (showing the last message for every chat) (image 1).

When a chat is not visible and a message is received in that chat the user will receive a notification (image 2)

At this point the problem starts. When the user clicks on the notification the chats listview seems to be broken. When the user receives another message after clicking on the notification his/her phone will vibrate but the chats listview doesn't change/update.

The other views do work after clicking the notification.

Below is a piece of the code that handles the listview update.

    protected PacketListener packetListener = new PacketListener() {

    @Override
    public void processPacket(Packet packet) {
        final Message message = (Message) packet;

        if (message.getBody() != null) {
            final String fromName = StringUtils.parseName(message.getFrom());

            runOnUiThread(new Runnable(){

                @Override
                public void run() {

                    Boolean seen = false;
                    if(ChatFragment.currentChat != null && ChatFragment.currentChat.getContact().getUsername().equals(fromName) && VisibilityHelper.appIsVisible() && VisibilityHelper.fragmentIsVisible(ChatFragment.fragmentClassName)) {
                        seen = true;
                    }

                    Long chat_id = chatsDataSource.getChatByContactId(contactsDataSource.getContactByUsername(fromName).getId()).getId();
                    ChatMessage newChatMessage = chatMessagesDataSource.insertChatMessage(chat_id, ConstantHelper.CHAT_MESSAGES_TYPE_RECEIVED, seen, DateHelper.getDatetime(), message.getBody());

                    Log.d("DEBUG", VisibilityHelper.sCurrentFragmentClassName);
                    if(VisibilityHelper.appIsVisible() && VisibilityHelper.fragmentIsVisible(ChatsFragment.fragmentClassName)) {
                        ChatsFragment chatsFragment = (ChatsFragment) getFragmentManager().findFragmentByTag(ChatsFragment.fragmentClassName);
                        Log.d("DEBUG", "REFRESHING");
                        chatsFragment.refreshChatsList();
                    }

                    if(!VisibilityHelper.appIsVisible()) {
                        notificationHelper.externalNotification(getBaseContext(), newChatMessage);
                    } else {
                        if(ChatFragment.currentChat != null && ChatFragment.currentChat.getContact().getUsername().equals(fromName) && VisibilityHelper.appIsVisible() && VisibilityHelper.fragmentIsVisible(ChatFragment.fragmentClassName)) {
                            ChatFragment.chatsList.add(newChatMessage);
                            ChatFragment.chatsList.notifyDataSetChanged();
                        } else {
                            notificationHelper.internalNotification(getBaseContext(), newChatMessage);
                        }
                    }
                }

            });
        }
    }

};

Main Activity (how the fragments are created)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_main);

    Bundle extras = getIntent().getExtras();

    initializeDataSources();
    setLastContactUpdate();

    if(usersDataSource.getCurrentUser() == null)
    {
        Intent LoginActivity = new Intent(getApplicationContext(), LoginActivity.class);
        LoginActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(LoginActivity);
        finish();
    }
    else
    {
        if(localServiceBinder == null)
            localServiceBinder = new LocalServiceBinder();

        bindLocalService();

        runDispatchMethod(extras, savedInstanceState);

        if (savedInstanceState == null) {
            getAndSaveUserDetails();
            GCMRegistrationID = registerGCM();
        }
    }
}

protected void runDispatchMethod(Bundle extras, Bundle savedInstanceState) {
    if(savedInstanceState == null) {
        startFragment(new ChatsFragment(), false);
    }

    if(extras != null && extras.getString(ConstantHelper.RECEIVED_MESSAGES_FROM) != null && !extras.getString(ConstantHelper.RECEIVED_MESSAGES_FROM).equals("")) {
        getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        startFragment(new ChatsFragment(), false);

        ChatFragment chatFragment = new ChatFragment();
        Bundle chatExtras = (Bundle) new Bundle();
        chatExtras.putString(ConstantHelper.RECEIVED_MESSAGES_FROM, extras.getString(ConstantHelper.RECEIVED_MESSAGES_FROM));
        chatFragment.setArguments(chatExtras);
        startFragment(chatFragment);
        return;
    }
}

public void startFragment(Fragment newFragment) {
    startFragment(newFragment, true);
}

public void startFragment(Fragment newFragment, Boolean addToBackstack) {
    if(addToBackstack) {
        getFragmentManager().beginTransaction().replace(R.id.container, newFragment, newFragment.getClass().getName()).addToBackStack(newFragment.getClass().getName()).commit();
    } else {
        getFragmentManager().beginTransaction().replace(R.id.container, newFragment, newFragment.getClass().getName()).commit();
    }

    getFragmentManager().executePendingTransactions();
}

Chats Adapter

public class ChatsAdapter extends BaseAdapter {

private List<Chat> chats;
private LayoutInflater inflater;
private String emptyLastMessageValue;

public ChatsAdapter(Context context, List<Chat> chats) {
    this.chats = chats;
    this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    emptyLastMessageValue = context.getText(R.string.fragment_chats_chat_created).toString();
}

@Override
public int getCount() {
    return chats.size();
}

@Override
public Chat getItem(int position) {
    return chats.get(position);
}

@Override
public long getItemId(int position) {
    return chats.get(position).getId();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View chatAdapterView = convertView;

    if(convertView == null)
        chatAdapterView = inflater.inflate(R.layout.adapter_chats, null);

    TextView layout_fullname = (TextView) chatAdapterView.findViewById(R.id.fullname);
    TextView layout_last_message = (TextView) chatAdapterView.findViewById(R.id.last_message);
    TextView layout_unread_messages = (TextView) chatAdapterView.findViewById(R.id.unread_messages);
    TextView layout_last_message_datetime = (TextView) chatAdapterView.findViewById(R.id.last_message_datetime);

    Chat row = chats.get(position);

    String firstname = row.getContact().getFirstname();
    String fullname = row.getContact().getFullname();

    if(firstname == null || firstname.equals(""))
        fullname = StringHelper.ucfirst(row.getContact().getUsername());

    layout_fullname.setText(fullname);

    String last_message_value;
    String last_message_datetime_value = "";
    if(row.getLastMessage() == null) {
        last_message_value = emptyLastMessageValue;
        layout_last_message_datetime.setVisibility(TextView.GONE);
        layout_unread_messages.setVisibility(TextView.GONE);
    } else {
        last_message_value = row.getLastMessage().getMessageBody();

        last_message_datetime_value = DateHelper.toReadableDatetime(row.getLastMessage().getMessageDatetime());
        layout_last_message_datetime.setText(last_message_datetime_value);

        Integer unreadMessages = row.getUnreadMessages();
        if(unreadMessages > 0) {
            layout_unread_messages.setText(String.valueOf(unreadMessages));
            layout_unread_messages.setVisibility(TextView.VISIBLE);
        } else {
            layout_unread_messages.setVisibility(TextView.GONE);
        }
    }

    layout_last_message.setText(last_message_value);

    return chatAdapterView;
}

}

Refresh Chats List function

public void refreshChatsList() {
    Log.d("DEBUG", "It does come here!");
    chats.clear();
    chats.addAll(((MainActivity) getActivity()).chatsDataSource.getAllChats());
    chats_adapter.notifyDataSetChanged();
}

Logcat output

07-06 20:12:43.892: D/SMACK(14948): 08:12:43 PM RCV  (1106735848): <message id="3jRRg-24" to="tijme@192.168.137.1" type="chat" from="test@192.168.137.1/Smack"><body>Test</body></message>

07-06 20:12:43.962: D/DEBUG(14948): nl.test.messatch.fragments.ChatsFragment

07-06 20:12:43.962: D/DEBUG(14948): REFRESHING

07-06 20:12:43.962: D/DEBUG(14948): It does come here!

07-06 20:12:48.406: D/SMACK(14948): 08:12:48 PM RCV  (1106735848): <message id="3jRRg-25" to="tijme@192.168.137.1" type="chat" from="test@192.168.137.1/Smack"><body>Test 2</body></message>

07-06 20:12:48.446: D/DEBUG(14948): nl.test.messatch.fragments.ChatsFragment

07-06 20:12:51.059: D/KUTZOOI(14948): HELEMAAL WÄCK

07-06 20:12:51.099: D/dalvikvm(14948): GC_FOR_ALLOC freed 161K, 3% free 9653K/9912K, paused 19ms, total 21ms

07-06 20:12:51.109: I/dalvikvm-heap(14948): Grow heap (frag case) to 13.272MB for 4000016-byte allocation

07-06 20:12:51.139: D/dalvikvm(14948): GC_FOR_ALLOC freed 1K, 2% free 13558K/13820K, paused 25ms, total 25ms

07-06 20:12:54.953: D/SMACK(14948): 08:12:54 PM RCV  (1106735848): <message id="3jRRg-26" to="tijme@192.168.137.1" type="chat" from="test@192.168.137.1/Smack"><body>Test 3</body></message>

07-06 20:12:54.993: D/DEBUG(14948): nl.test.messatch.fragments.ChatsFragment

07-06 20:12:54.993: D/DEBUG(14948): REFRESHING

07-06 20:12:54.993: D/DEBUG(14948): It does come here!

Chats List Chats listview

Notification Chat notification

Tijme
  • 39
  • 2
  • 24
  • 41
  • Are you sure you're working with the same chat fragment instance after clicking the notification? – user Jul 12 '14 at 16:12
  • Nope. I use findFragmentByTag to find the fragment and findFragmentByTag does not return null, so it does find a fragment. It should be the same ofcourse... – Tijme Jul 12 '14 at 16:25
  • Can you post the adapter's code? – user Jul 12 '14 at 16:28
  • @Luksprog's suggestion seems reasonable. Could you add a no-arg constructor to your chat list fragment, logging its hashCode()? See how many instances of that fragment are created when the activity is resumed due to the nofitication, just to be sure. It may happen, depending on how and where and when you are adding fragments, that you end up with two instances of the same fragment, one over the other, and the one you modify is covered but the one that is not modified. Uh, also: your static variables usage makes me sick. – Giulio Piancastelli Jul 12 '14 at 16:47
  • @GiulioPiancastelli Ahhh I see two different hash codes after opening the notification. What can I do about it? An could you explain how to avoid static variables? I am a bit new to Android! – Tijme Jul 12 '14 at 17:27
  • You could explain how you setup the chat fragment along with the code you used for this. – user Jul 12 '14 at 17:29
  • @Luksprog I added a piece of my MainActivity where the fragments are created. – Tijme Jul 12 '14 at 17:33
  • As a test, in `startFragment(Fragment newFragment, Boolean addToBackstack)`, you could add a if condition to test if the `ChatFragment` is already in the layout and abort the transaction if it is(add this at the start of the method): `if (getFragmentManager().findFragmentByTag(ChatsFragment.fragmentClassName) != null) {return;}` and see how it goes. Also, as ChatList is supposed to hold a list of the chat messages, it's not quite clear why you are doing a lot of transaction with this fragment in many places. – user Jul 12 '14 at 17:38
  • @Luksprog Well, that helped. It updates now. But the hashes are still different (before and after the notification click). Why doesn't my app resume instead of restarting while clicking on a notification? What do you mean with transactions, and how can I reduce this? – Tijme Jul 12 '14 at 17:48
  • I assumed that you call those `startFragment()` methods from other points in your code, which will result in adding a new chat fragment every time(the transactions). For example, in the `runDispatchMethod()` method you could end up calling `startFragment()` three times(so you'll add three ChatFragment instance when one will do(or if the ChatFragment is already in the layout you could avoid adding a new ChatFragment)), you don't have any reasons to do this so rethink your code. Also, see http://stackoverflow.com/questions/5605207/android-notification-restarts-app-but-want-to-resume – user Jul 12 '14 at 18:01
  • @Luksprog runDispatchMethod is only called onCreate() in the MainActivity (also visible in the code above). I am going to take a look at the link you posted right now :) – Tijme Jul 12 '14 at 18:04
  • @Luksprog Well i'ts fixed! Thanks to the link you posted I came across this topic http://stackoverflow.com/questions/3356095/how-to-bring-android-existing-activity-to-front-via-notification?answertab=votes#tab-top The first answer fixed it for me. I now continue the app instead of "re-starting it" :) – Tijme Jul 12 '14 at 18:21
  • have to try to call refreshChatsList() method form onResume() of activity. – Imtiyaz Khalani Jul 17 '14 at 07:21
  • @Imtiyaz I tried but that does not work – Tijme Jul 17 '14 at 17:18
  • @Luksprog well, if you write your answer below you will get +50 reputation :) – Tijme Jul 19 '14 at 13:39

2 Answers2

2

Make sure that you are working with the same fragment after returning to the activity through clicking a notification(judging by the way the problem appears). If you don't hold a reference to the same instance that the user sees, then any chat update will fail silently as you'll be updating only the background fragment(and not the visible one). The error seems to be in the runDispatchMethod() where you do a transaction with a new fragment every time not checking if another instance of the ChatsFragment is already available(in which case you would just call an update method on that instance).

user
  • 86,916
  • 18
  • 197
  • 190
1

(I would put a comment if I could)

I think the problem is the notifyingdatasetChanged();

Add this to your adapter

public void swapItems(List<Chat> chat) {
    this.chats = chat;
    notifyDataSetChanged();
}

Call this for new messages

chats_adapter.swapItems(((MainActivity) getActivity()).chatsDataSource.getAllChats());
johng
  • 825
  • 1
  • 9
  • 15