Android Open Source - AndroidMessenger Conversation Container






From Project

Back to project page AndroidMessenger.

License

The source code is released under:

GNU General Public License

If you think the Android project AndroidMessenger listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * @author - Zachary Goldberg @ 2008//  w ww . j  a  v a  2s.  c  o m
 */

package com.penn.cis121.androidmessenger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentReceiver;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.ViewSwitcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup.LayoutParams;

public class ConversationContainer extends Activity {
  private List<Button> buttons;
  private List<String> windows;
  private HashMap<String, String> editing; // Buddy -> currently editing
  // text
  private HashMap<String, List<String>> messages; // buddy -> message
  // conversation
  private HashMap<String, String> nameFromAlias;

  /* Store information on the connection for each buddy */
  private Map<String, String> buddyToMe;
  private Map<String, Integer> buddyToConnectionId;

  private String currentBuddy;
  private ButtonListener bl;
  private String lastMessage = "";
  private Logger logger;
  private LinearLayout currentRow;
  private LinearLayout rowContainer;
  private EditText input;

  private ScrollView scroller;
  private Button closeButton;

  private ConversationReciever aml;


  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    logger = Logger.getLogger(this.getClass().getName());
    
    setupUIComponents();
    
    aml = new ConversationReciever();
    registerReceiver(aml, new IntentFilter("AndroidMessenger"));

    windows = new ArrayList<String>();
    editing = new HashMap<String, String>();
    messages = new HashMap<String, List<String>>();
    nameFromAlias = new HashMap<String, String>();
    buttons = new ArrayList<Button>();
    buddyToMe = new HashMap<String, String>();
    buddyToConnectionId = new HashMap<String, Integer>();
    
    makeWindows();
    reloadPrefs();
    
    Intent i = getIntent();
    Bundle bb = i.getExtras();
    String alias = bb.getString("alias");
    String name = bb.getString("name");
    String me =  i.getStringExtra("username");
    
    if (alias != null && name != null) {
      if(!windows.contains(name)){
        buddyToMe.put(name, me);
        buddyToConnectionId.put(me,bb.getInt("connectionId"));
        nameFromAlias.put(alias, name);
        messages.put(name, new LinkedList<String>());
        editing.put(name, "");
        Button b = addButton(name);
        b.performClick(); //fire the onFocusChanged        
      }else{
        for(Button b : buttons){
          if(b.getText().toString().equals(name)){
            b.performClick();
          }
        }
      }
      currentBuddy = name;
    }else{
      finish(); //Something not good happened
    }  
        
    /*
     * Handle any messages received prior to opening the conversation
     * panel.
     */
    for (int x = 0; x < bb.getInt("numwaiting"); x++) {
      Bundle b = bb.getBundle(x + "");
      handleMessage(b.getString("name"), b.getString("message"), b
          .getString("username"), b.getInt("connectionId"));
      logger.severe(b.getString("name") + "," + b.getString("message"));
    }
    
    regenLayout();
  }
  public void onDestroy(){
    super.onDestroy();
    unregisterReceiver(aml);
  }
  
  
  private void setupUIComponents(){
    setContentView(R.layout.conversationcontainer);
    bl = new ButtonListener();
    rowContainer = (LinearLayout) findViewById(R.id.buttonRows);
    closeButton = (Button) findViewById(R.id.closeButton);
    closeButton.setOnClickListener(bl);
    scroller = (ScrollView) findViewById(R.id.scrollBox);
    input = (EditText) findViewById(R.id.inputBox);
    input.setOnKeyListener(bl);
  }

  private Button addButton(String text) {
    logger.severe("Adding button for " + text);
    Button nb = new Button(this);
    nb.setText(text);
    setProps(nb);
    if (currentRow == null || currentRow.getChildCount() == 3) {
      LinearLayout newrow = new LinearLayout(this);
      newrow.setLayoutParams(new LinearLayout.LayoutParams(
          LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
      newrow.setOrientation(LinearLayout.HORIZONTAL);
      rowContainer.addView(newrow);
      currentRow = newrow;
    }
    currentRow.addView(nb);
    rowContainer.requestLayout();
    windows.add(text);
    return nb;
  }

  private void removeButton(String name) {
    if (currentRow == null)
      return;
    // Easiest way to do this is just going to be to entirely redo the buttons
    if (!windows.remove(name)) {
      logger.severe("Problem removing " + name);
    }
    buttons.clear();
    currentRow = null;
    rowContainer.removeAllViews();
    List<String> oldWindows = new ArrayList<String>();
    for (String s : windows)
      oldWindows.add(s);
    windows.clear();
    for (String s : oldWindows) {
      addButton(s);
    }
  }

  private void setProps(Button b) {
    b.setOnClickListener(bl);
    b.setOnFocusChangeListener(bl);
    if (windows.size() >= 3 && b.getText().toString().length() > 10) {
      b.setText(b.getText().toString().substring(0, 10) + "..");
    }
    b.setLayoutParams(new LinearLayout.LayoutParams(
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
    buttons.add(b);
  }

  private void regenLayout() {
    TextView messageBox = (TextView) findViewById(R.id.messageBox);
    messageBox.setText("");
    if (messages.get(currentBuddy) == null) {
      logger.warning(currentBuddy + " has no message list?");
      return;
    }
    String newText="";
    for (String s : messages.get(currentBuddy)) {
      newText += s;      
    }
    messageBox.setText(newText);    
    EditText editBox = (EditText) findViewById(R.id.inputBox);
    editBox.setText(editing.get(currentBuddy));    
    /*
     * I could not get this to ever scroll all the way to the bottom.  The problem is that the UI 
     * has not yet updated to the new text.  So it scrolls to the bottom of the text pre-new text.
     * I've spent hours trying to find a hack.  I encourage you to try and best me.  Things i've tried:
     * 
     * Playing with requestLayout,computeScroll(),invalidate() and notify() on both the messageBox and scroller objects
     * Setting up a TextWatcher on the messageBox to reset the scroll onTextChanged
     * Setting up a new thread and setting a timer in that thread.  When the timer expires try and update the window (Give the UI time to update).
     *          Does not work because you can't update the UI from another thread
     *          So I tried to set an uncaughtExceptionHandler on the thread and then in the thread divide by zero to throw an exception.
     *          Then 'this' thread would have the uncaughtExceptionHandler and be able to update the UI.
     *          This caused all sorts of BAD behavior in many many many ways.  Don't try it.
     *          
     * Labeled as an Android Deficiency for now.
     */
    messageBox.requestLayout();
    messageBox.invalidate();
    scroller.fullScroll(ScrollView.FOCUS_DOWN);    
  }

  private String currentInput() {
    EditText editBox = (EditText) findViewById(R.id.inputBox);
    return editBox.getText().toString();
  }

  private void handleMessage(String buddyName, String message,
      String username, int connectionId) {
    
    if (!buddyToMe.containsKey(buddyName))
      buddyToMe.put(buddyName, username);
    if(!buddyToConnectionId.containsKey(username)){
      buddyToConnectionId.put(username,connectionId);
    }    
    if (!messages.containsKey(buddyName))
      messages.put(buddyName, new LinkedList<String>());
    
    lastMessage = buddyName + ": " + message + "\n";
    messages.get(buddyName).add(lastMessage);
    boolean found = false;
    logger.warning(buddyName + "," + currentBuddy + ","  + (buddyName.equals(currentBuddy)));
    if (!buddyName.equals(currentBuddy)) {
      for (Button b : buttons)
        // Find the right button and add a *
        if (b.getText().equals(buddyName)) {
          b.setText("*" + buddyName);
          found = true;
        }
      if (!found)
        addButton("*" + buddyName);
    }
    regenLayout();
  }

  private class ButtonListener implements OnFocusChangeListener,
      OnClickListener, OnKeyListener {

    public void onFocusChanged(View v, boolean hasFocus) {
      // Set all buttons to not focusable
      if (hasFocus == true) {
        for (Button b : buttons) {
          if (!b.getText().equals(((Button) v).getText()))
            b.clearFocus();
        }
      }
      // Save the current input
      editing.put(currentBuddy, currentInput());
      Button b = (Button) v;
      if (b.getText().charAt(0) == '*') {
        b.setText(b.getText().toString().substring(1)); // get rid of the *
      }
      currentBuddy = nameFromAlias.get(b.getText());
      if (currentBuddy == null)
        currentBuddy = b.getText().toString();
      TextView tv = (TextView) findViewById(R.id.status);
      tv.setText("Conversation with " + currentBuddy);
      regenLayout();
    }
    public void onClick(View button) {
      if (button != closeButton){
        onFocusChanged(button, true);
      }else {
        if (windows.size() <= 1) { 
          //Close the whole panel if we're closing the last buddy
          windows.remove(currentBuddy);  
          finish();
          return;
        }
        removeButton(currentBuddy);
        currentBuddy = windows.get(0);
        for (Button bv : buttons) {
          if (bv.getText().equals(currentBuddy))
            bv.requestFocus();
        }
      }
    }
    public boolean onKey(View view, int keyCode, KeyEvent event) {
      //We only want key events for the input window
      if (view != input)
        return false;
      //For debugging
      if (keyCode == KeyEvent.KEYCODE_MINUS){
        scroller.fullScroll(ScrollView.FOCUS_DOWN);
      }
      //If we actually want to send a message
      if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
          || keyCode == KeyEvent.KEYCODE_NEWLINE) {
        String from = buddyToMe.get(currentBuddy);
        String msg = input.getText().toString();
        if (msg.length() == 0)
          return false;
        if((from + ": " +msg + "\n").equals(lastMessage))
          return false;
        Intent i = new Intent();
        i.setAction("AndroidMessenger");
        i.putExtra("purpose", "sendMessage");
        i.putExtra("connectionId",buddyToConnectionId.get(from));
        i.putExtra("message", msg);
        i.putExtra("buddyName", currentBuddy);
        broadcastIntent(i);
        input.setText("");
        lastMessage = from + ": " + msg + "\n";
        messages.get(currentBuddy).add(lastMessage);
        regenLayout();
        return true;
      }
      return false;
    }
  }
  private class ConversationReciever extends IntentReceiver {
    @Override
    public void onReceiveIntent(Context arg0, Intent intent) {
      String purpose = intent.getStringExtra("purpose");
      if (purpose.equals("messageRecieved")) {
        handleMessage( intent.getStringExtra("name"),
            intent.getStringExtra("message"),
            intent.getStringExtra("username"), 
            intent.getIntExtra("connectionId",0)
            );
      }
    }

  }
  
  /*
   * The below functions can be classified as 
   * "Terrible horrible ugly bad never do this its no good HACK" 
   * (Only the first 3 are used.  The second 2 are for if we could onFreeze() but we can't)
   * (The actual hack is using SharedPreferences to emulate persistent memory)
   * 
   * 
   *  I've had some discussion with Android developers on IRC and read
   *  lots of documentation; and thus far there is no _good_ way of maintaining state
   *  in between switches back and fourth from conversation page to the 
   *  main buddy list.  Hence every time we re-open the conversation pain we need to
   *  rebuild all of the UI from saved (serialized) state.  The below code does that 
   *  saving and serialization.  Even the way we're saving data isn't the best way
   *  and likely leaks a bit.  Its just _bad_.  But as the SDK develops i'm sure
   *  a better solution will arise.  For now we leave it.
   * 
   */
  private void makeWindows(){
    SharedPreferences prefs = getSharedPreferences(getClass().toString()+"windows", 0);
     Map<String,? extends Object> map = prefs.getAll();
     for(String s : map.keySet()){
       logger.severe("Adding button for " + s);
       addButton(s);
     }      
  }
  public void reloadPrefs() {      
     SharedPreferences prefs = getSharedPreferences(getClass().toString()+"editing", 0);
     Map<String,? extends Object> map = prefs.getAll();
         for(String buddy : map.keySet()){
           editing.put(buddy, (String)map.get(buddy));                       
         }
         prefs = getSharedPreferences(getClass().toString()+"aliases", 0);
         map = prefs.getAll();
         for(String buddy : map.keySet()){
           nameFromAlias.put(buddy, (String)map.get(buddy));                       
         }
         prefs = getSharedPreferences(getClass().toString()+"tome", 0);
         map = prefs.getAll();
         for(String buddy : map.keySet()){
           buddyToMe.put(buddy, (String)map.get(buddy));                       
         }
         prefs = getSharedPreferences(getClass().toString()+"meid", 0);
         map = prefs.getAll();
         for(String buddy : map.keySet()){
           buddyToConnectionId.put(buddy, (Integer)map.get(buddy));                       
         }
         for(String buddy : windows){
             prefs = getSharedPreferences(getClass().toString()+"messages"+buddy, 0);
             map = prefs.getAll();
             if(!messages.containsKey(buddy))
               messages.put(buddy,new ArrayList<String>());
             logger.info("Restoring messages for " + buddy + " (#" + map.keySet().size()+")");
             for(String message : map.keySet()){
               messages.get(buddy).add(message);                       
             }
         }
  }
  
    @Override
    protected void onPause() {
        super.onPause();        
        SharedPreferences.Editor editor = getSharedPreferences(getClass().toString()+"windows", 0).edit();        
        for(String s : windows){
          editor.putString(s,s);
        }
        editor.commit();
        editor = getSharedPreferences(getClass().toString()+"aliases", 0).edit();
        for(String buddy : nameFromAlias.keySet()){                      
            editor.putString(buddy,nameFromAlias.get(buddy));                       
        }
        editor.commit();
        editor = getSharedPreferences(getClass().toString()+"tome", 0).edit();
        for(String buddy : buddyToMe.keySet()){                      
            editor.putString(buddy,buddyToMe.get(buddy));                       
        }
        editor.commit();
        editor = getSharedPreferences(getClass().toString()+"meid", 0).edit();
        for(String buddy : buddyToConnectionId.keySet()){                      
            editor.putInt(buddy,buddyToConnectionId.get(buddy));                       
        }
        editor.commit();
        editor = getSharedPreferences(getClass().toString()+"editing", 0).edit();
        for(String buddy : editing.keySet()){                      
            editor.putString(buddy,editing.get(buddy));                       
        }
        editor.commit();
        for(String buddy : messages.keySet()){
            editor = getSharedPreferences(getClass().toString()+"messages"+buddy, 0).edit();
            logger.info("Saving messages for " + buddy + " (#" + messages.get(buddy).size()+")");
            for(String msg : messages.get(buddy))
              editor.putString(msg,"");
            editor.commit();            
        }        
    }
    /*  
  private void restoreFromBundle(Bundle state) {
    rowContainer.removeAllViews();
    
    Bundle windowBundle = state.getBundle("windowBundle");
    Bundle NFABundle = state.getBundle("NFABundle");
    Bundle BTMBundle = state.getBundle("BTMBundle");
    Bundle MPWBundle = state.getBundle("MPWBundle");
    Bundle MClassBundle = state.getBundle("MClassBundle");
    Bundle EditingBundle = state.getBundle("EditingBundle");
    Bundle MessageBundle = state.getBundle("MessageBundle");
    
    for (String s : windowBundle.keySet())
      addButton(s);
    
    for (String buddy : EditingBundle.keySet())
      editing.put(buddy, (String) EditingBundle.getString(buddy));
    
    for (String buddy : NFABundle.keySet())
      nameFromAlias.put(buddy, (String) NFABundle.getString(buddy));
    
    for (String buddy : BTMBundle.keySet())
      buddyToMe.put(buddy, (String) BTMBundle.getString(buddy));
    
    for (String buddy : MPWBundle.keySet())
      mePw.put(buddy, (String) MPWBundle.getString(buddy));
    
    for (String buddy : MClassBundle.keySet())
      meClass.put(buddy, (String) MClassBundle.getString(buddy));
    
    for (String buddy : MessageBundle.keySet()) {
      if (!messages.containsKey(buddy))
        messages.put(buddy, new ArrayList<String>());
      messages.get(buddy).add(MessageBundle.getString(buddy));
    }
  }

  /**
   * Any time we are paused we need to save away the current state, so it will
   * be restored correctly when we are resumed.
   *//*
  @Override
  protected void onFreeze(Bundle outBundle) {
    logger.severe("Inside onFreeze");
    Bundle windowBundle = new Bundle();
    Bundle NFABundle = new Bundle();
    Bundle BTMBundle = new Bundle();
    Bundle MPWBundle = new Bundle();
    Bundle MClassBundle = new Bundle();
    Bundle EditingBundle = new Bundle();
    Bundle MessageBundle = new Bundle();
    for (String s : windows)
      windowBundle.putString(s, s);
    for (String buddy : nameFromAlias.keySet())
      NFABundle.putString(buddy, nameFromAlias.get(buddy));
    for (String buddy : buddyToMe.keySet())
      BTMBundle.putString(buddy, buddyToMe.get(buddy));
    for (String buddy : mePw.keySet())
      MPWBundle.putString(buddy, mePw.get(buddy));
    for (String buddy : meClass.keySet())
      MClassBundle.putString(buddy, meClass.get(buddy));
    for (String buddy : editing.keySet())
      EditingBundle.putString(buddy, editing.get(buddy));
    for (String buddy : messages.keySet()) {
      String big = "";
      for (String msg : messages.get(buddy))
        big += msg;
      MessageBundle.putString(buddy, big);
    }
    outBundle.putBundle("windowBundle", windowBundle);
    outBundle.putBundle("NFABundle", NFABundle);
    outBundle.putBundle("BTMBundle", BTMBundle);
    outBundle.putBundle("MPWBundle", MPWBundle);
    outBundle.putBundle("MClassBundle", MClassBundle);
    outBundle.putBundle("EditingBundle", EditingBundle);
    outBundle.putBundle("MessageBundle", MessageBundle);
    super.onFreeze(outBundle);
  }*/
}




Java Source Code List

com.levelonelabs.aim.AIMAdapter.java
com.levelonelabs.aim.AIMBuddy.java
com.levelonelabs.aim.AIMClient.java
com.levelonelabs.aim.AIMGroup.java
com.levelonelabs.aim.AIMListener.java
com.levelonelabs.aim.AIMSender.java
com.levelonelabs.aim.XMLizable.java
com.levelonelabs.aim.example.AIMReceptionist.java
com.levelonelabs.aim.example.EchoExample.java
com.penn.cis121.androidmessenger.AMProtocolMapper.java
com.penn.cis121.androidmessenger.AccountEditor.java
com.penn.cis121.androidmessenger.AndroidMessenger.java
com.penn.cis121.androidmessenger.ConversationContainer.java
com.penn.cis121.androidmessenger.accountprovider.AccountInfo.java
com.penn.cis121.androidmessenger.accountprovider.AccountProvider.java
com.penn.cis121.androidmessenger.protocols.Aim.java