Hedgeroid: Lobby activity improvements
authorMedo <smaxein@googlemail.com>
Thu, 19 Jul 2012 22:55:36 +0200
changeset 7346 b0f67c5b4215
parent 7344 25b8906f901a
child 7349 12fdfd2038d4
Hedgeroid: Lobby activity improvements
project_files/Android-build/SDL-android-project/res/layout/activity_lobby.xml
project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml
project_files/Android-build/SDL-android-project/res/layout/lobby_players_fragment.xml
project_files/Android-build/SDL-android-project/res/layout/lobby_rooms_fragment.xml
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatlogAdapter.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyChatFragment.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/MessageLog.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomList.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java
--- a/project_files/Android-build/SDL-android-project/res/layout/activity_lobby.xml	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_lobby.xml	Thu Jul 19 22:55:36 2012 +0200
@@ -1,61 +1,68 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:padding="5dp" >
-
-    <FrameLayout
-        android:layout_width="fill_parent"
-        android:layout_height="0dp"
-        android:layout_marginBottom="10dp"
-        android:layout_weight="0.3"
-        android:background="@drawable/box" >
-
-        <fragment
-            android:id="@+id/roomListFragment"
-            android:layout_width="fill_parent"
-            android:layout_height="fill_parent"
-            class="org.hedgewars.hedgeroid.netplay.RoomlistFragment"
-            tools:layout="@layout/lobby_rooms_fragment" />
-    </FrameLayout>
-
-    <LinearLayout
-        android:layout_width="fill_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.7"
-        android:baselineAligned="false"
-        android:orientation="horizontal" >
+<FrameLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+  	android:layout_width="fill_parent"
+	android:layout_height="fill_parent">
+	<include layout="@layout/background"/>
 
-        <FrameLayout
-            android:layout_width="0dp"
-            android:layout_height="fill_parent"
-            android:layout_marginRight="10dp"
-            android:layout_weight="0.7"
-            android:background="@drawable/box" >
-
-            <fragment
-                android:id="@+id/chatFragment"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent"
-                class="org.hedgewars.hedgeroid.netplay.LobbyChatFragment"
-                tools:layout="@layout/lobby_chat_fragment" />
-        </FrameLayout>
-
-        <FrameLayout
-            android:layout_width="0dp"
-            android:layout_height="fill_parent"
-            android:layout_weight="0.3"
-            android:background="@drawable/box" >
-
-            <fragment
-                android:id="@+id/playerListFragment"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent"
-                class="org.hedgewars.hedgeroid.netplay.PlayerlistFragment"
-                tools:layout="@layout/lobby_players_fragment" />
-        </FrameLayout>
-    </LinearLayout>
-
-</LinearLayout>
\ No newline at end of file
+	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	    xmlns:tools="http://schemas.android.com/tools"
+	    android:layout_width="match_parent"
+	    android:layout_height="match_parent"
+	    android:orientation="vertical"
+	    android:padding="5dp" >
+	
+	    <FrameLayout
+	        android:layout_width="fill_parent"
+	        android:layout_height="0dp"
+	        android:layout_marginBottom="10dp"
+	        android:layout_weight="0.3"
+	        android:background="@drawable/box" >
+	
+	        <fragment
+	            android:id="@+id/roomListFragment"
+	            android:layout_width="fill_parent"
+	            android:layout_height="fill_parent"
+	            class="org.hedgewars.hedgeroid.netplay.RoomlistFragment"
+	            tools:layout="@layout/lobby_rooms_fragment" />
+	    </FrameLayout>
+	
+	    <LinearLayout
+	        android:layout_width="fill_parent"
+	        android:layout_height="0dp"
+	        android:layout_weight="0.7"
+	        android:baselineAligned="false"
+	        android:orientation="horizontal" >
+	
+	        <FrameLayout
+	            android:layout_width="0dp"
+	            android:layout_height="fill_parent"
+	            android:layout_marginRight="10dp"
+	            android:layout_weight="0.7"
+	            android:background="@drawable/box" >
+	
+	            <fragment
+	                android:id="@+id/chatFragment"
+	                android:layout_width="fill_parent"
+	                android:layout_height="fill_parent"
+	                class="org.hedgewars.hedgeroid.netplay.LobbyChatFragment"
+	                tools:layout="@layout/lobby_chat_fragment" />
+	        </FrameLayout>
+	
+	        <FrameLayout
+	            android:layout_width="0dp"
+	            android:layout_height="fill_parent"
+	            android:layout_weight="0.3"
+	            android:background="@drawable/box" >
+	
+	            <fragment
+	                android:id="@+id/playerListFragment"
+	                android:layout_width="fill_parent"
+	                android:layout_height="fill_parent"
+	                class="org.hedgewars.hedgeroid.netplay.PlayerlistFragment"
+	                tools:layout="@layout/lobby_players_fragment" />
+	        </FrameLayout>
+	    </LinearLayout>
+	
+	</LinearLayout>
+</FrameLayout>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml	Thu Jul 19 22:55:36 2012 +0200
@@ -5,23 +5,15 @@
     android:layout_height="match_parent"
     android:orientation="vertical" >
 
-	<ScrollView
-        android:id="@+id/lobbyConsoleScroll"
+    <ListView 
+        android:id="@+id/lobbyConsole"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"
-        android:requiresFadingEdge="vertical"
-        android:scrollbars="vertical" >
-
-        <TextView
-            android:id="@+id/lobbyConsole"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:autoLink="none"
-            android:freezesText="true"
-            android:text=""
-            tools:context=".LobbyActivity" />
-    </ScrollView>
+        android:layout_weight="1" 
+        android:clickable="false"
+        android:transcriptMode="normal"
+        android:stackFromBottom="true"
+        />
 
 	<EditText
         android:id="@+id/lobbyChatInput"
--- a/project_files/Android-build/SDL-android-project/res/layout/lobby_players_fragment.xml	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_players_fragment.xml	Thu Jul 19 22:55:36 2012 +0200
@@ -12,7 +12,6 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:drawSelectorOnTop="false"
-        android:animateLayoutChanges="true"
         tools:listitem="@android:layout/simple_list_item_1" />
 
     <TextView
--- a/project_files/Android-build/SDL-android-project/res/layout/lobby_rooms_fragment.xml	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_rooms_fragment.xml	Thu Jul 19 22:55:36 2012 +0200
@@ -12,7 +12,6 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:drawSelectorOnTop="false"
-        android:animateLayoutChanges="true"
         tools:listitem="@android:layout/simple_list_item_2" />
 
     <TextView
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ChatlogAdapter.java	Thu Jul 19 22:55:36 2012 +0200
@@ -0,0 +1,92 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.netplay.MessageLog.Observer;
+
+import android.content.Context;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.LayoutParams;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+/**
+ * A simple TextView that remembers its contents to avoid having to
+ * re-layout them.
+ */
+class LoglineView extends TextView {
+	CharSequence chatlogAdapterText;
+	
+	public LoglineView(Context context) {
+		super(context);
+	}
+}
+
+public class ChatlogAdapter extends BaseAdapter implements Observer {
+	long idOffset = 0;
+	private List<CharSequence> log = new ArrayList<CharSequence>();
+	private Context context;
+	
+	public ChatlogAdapter(Context context) {
+		this.context = context;
+	}
+	
+	public int getCount() {
+		return log.size();
+	}
+
+	public Object getItem(int position) {
+		return log.get(position);
+	}
+
+	public long getItemId(int position) {
+		return position+idOffset;
+	}
+
+	public boolean hasStableIds() {
+		return true;
+	}
+
+	public void clear() {
+		idOffset += log.size();
+		log.clear();
+		notifyDataSetChanged();
+	}
+	
+	public void lineAdded(CharSequence text) {
+		log.add(text);
+		notifyDataSetChanged();
+	}
+	
+	public void lineRemoved() {
+		log.remove(0);
+		idOffset += 1;
+		notifyDataSetChanged();
+	}
+	
+	public void setLog(Collection<CharSequence> log) {
+		idOffset += log.size();
+		this.log = new ArrayList<CharSequence>(log);
+		notifyDataSetChanged();
+	}
+	
+	public View getView(int position, View convertView, ViewGroup parent) {
+		LoglineView v = (LoglineView)convertView;
+		CharSequence line = log.get(position);
+		if (v == null) {
+			v = new LoglineView(context);
+			v.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
+			v.setMovementMethod(LinkMovementMethod.getInstance());
+		}
+		if(line != v.chatlogAdapterText) {
+			v.setText(line);
+			v.chatlogAdapterText = line;
+		}
+		return v;
+	}
+}
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyChatFragment.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/LobbyChatFragment.java	Thu Jul 19 22:55:36 2012 +0200
@@ -8,63 +8,60 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.res.Configuration;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.support.v4.app.Fragment;
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
 import android.widget.EditText;
-import android.widget.ScrollView;
+import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
 public class LobbyChatFragment extends Fragment {
-	private TextView textView;
 	private EditText editText;
-	private ScrollView scrollView;
+	private ListView listView;
+	private ChatlogAdapter adapter;
 	private Netconn netconn;
 	
-	private void scrollDown() {
-		scrollView.post(new Runnable() {
-			public void run() {
-				scrollView.smoothScrollTo(0, textView.getBottom());
-			}
-		});
-	}
-	
 	private void commitText() {
-		if(netconn != null && netconn.isConnected()) {
-			String text = editText.getText().toString();
+		String text = editText.getText().toString();
+		if(netconn != null && netconn.isConnected() && text.length()>0) {
 			editText.setText("");
 			netconn.sendChat(text);
 		}
 	}
 	
 	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		adapter = new ChatlogAdapter(getActivity());
+	}
+	
+	@Override
 	public void onStart() {
 		super.onStart();
 		getActivity().bindService(new Intent(getActivity(), NetplayService.class), serviceConnection,
 	            Context.BIND_AUTO_CREATE);
-		Log.d("LobbyChatFragment", "started");
 	}
 	
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 		View view = inflater.inflate(R.layout.lobby_chat_fragment, container, false);
-		textView = (TextView) view.findViewById(R.id.lobbyConsole);
 		editText = (EditText) view.findViewById(R.id.lobbyChatInput);
-		scrollView = (ScrollView) view.findViewById(R.id.lobbyConsoleScroll);
+		listView = (ListView) view.findViewById(R.id.lobbyConsole);
 		
-		textView.setMovementMethod(LinkMovementMethod.getInstance());
+		listView.setAdapter(adapter);
+		listView.setDivider(null);
+		listView.setDividerHeight(0);
+		listView.setVerticalFadingEdgeEnabled(true);
 		
         editText.setOnEditorActionListener(new OnEditorActionListener() {
 			public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@@ -90,26 +87,14 @@
         public void onServiceConnected(ComponentName className, IBinder binder) {
         	Log.d("LobbyChatFragment", "netconn received");
         	netconn = ((NetplayBinder) binder).getNetconn();
-        	netconn.lobbyLog.observe(observer);
+        	adapter.setLog(netconn.lobbyLog.getLog());
+        	netconn.lobbyLog.registerObserver(adapter);
         }
 
         public void onServiceDisconnected(ComponentName className) {
         	// TODO navigate away
-        	netconn.lobbyLog.unobserve(observer);
+        	netconn.lobbyLog.unregisterObserver(adapter);
         	netconn = null;
         }
     };
-    
-    private MessageLog.Observer observer = new MessageLog.Observer() {
-		public void textChanged(Spanned text) {
-			if(textView != null) {
-				int overhang = textView.getHeight()-scrollView.getHeight();
-				boolean followBottom = overhang<=0 || Math.abs(overhang-scrollView.getScrollY())<5;
-				textView.setText(text);
-				if(followBottom) {
-					scrollDown();
-				}
-			}
-		}
-	};
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/MessageLog.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/MessageLog.java	Thu Jul 19 22:55:36 2012 +0200
@@ -1,5 +1,7 @@
 package org.hedgewars.hedgeroid.netplay;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
@@ -22,7 +24,7 @@
 import android.util.Log;
 
 public class MessageLog {
-	private static final int BACKLOG_CHARS = 10000;
+	private static final int BACKLOG_LINES = 200;
 	
 	private static final int INFO_COLOR = Color.GRAY;
 	private static final int CHAT_COLOR = Color.GREEN;
@@ -32,9 +34,7 @@
 	
 	private final Context context;
 	private List<Observer> observers = new LinkedList<Observer>();
-	
-	private SpannableStringBuilder log = new SpannableStringBuilder();
-	private List<Integer> lineLengths = new LinkedList<Integer>();
+	private List<CharSequence> log = new LinkedList<CharSequence>();
 	
 	public MessageLog(Context context) {
 		this.context = context;
@@ -61,18 +61,20 @@
 	
 	private void append(CharSequence msg) {
 		SpannableStringBuilder ssb = new SpannableStringBuilder();
-		ssb.append(makeLogTime()).append(msg).append("\n");
+		ssb.append(makeLogTime()).append(msg);
 		appendRaw(ssb);
 	}
 	
 	private void appendRaw(CharSequence msg) {
-		lineLengths.add(msg.length());
-		log.append(msg);
-		while(log.length() > BACKLOG_CHARS) {
-			log.delete(0, lineLengths.remove(0));
+		if(log.size() > BACKLOG_LINES) {
+			log.remove(0);
+			for(Observer o : observers) {
+				o.lineRemoved();
+			}
 		}
+		log.add(msg);
 		for(Observer o : observers) {
-			o.textChanged(log);
+			o.lineAdded(msg);
 		}
 	}
 	
@@ -113,7 +115,7 @@
 			append(msg);
 			break;
 		case JnaFrontlib.NETCONN_MSG_TYPE_SERVERMESSAGE:
-			appendRaw(span(TextUtils.concat("\n", Html.fromHtml(msg), "\n\n"), new RelativeSizeSpan(1.5f)));
+			appendRaw(span(TextUtils.concat("\n", Html.fromHtml(msg), "\n"), new RelativeSizeSpan(1.5f)));
 			break;
 		default:
 			Log.e("MessageLog", "Unknown messagetype "+type);
@@ -121,19 +123,27 @@
 	}
 	
 	void clear() {
+		for(Observer o : observers) {
+			o.clear();
+		}
 		log.clear();
-		lineLengths.clear();
 	}
 	
-	public void observe(Observer o) {
+	public void registerObserver(Observer o) {
 		observers.add(o);
 	}
 	
-	public void unobserve(Observer o) {
+	public void unregisterObserver(Observer o) {
 		observers.remove(o);
 	}
 	
 	public static interface Observer {
-		void textChanged(Spanned text);
+		void lineAdded(CharSequence text);
+		void lineRemoved();
+		void clear();
+	}
+
+	public Collection<CharSequence> getLog() {
+		return Collections.unmodifiableList(log);
 	}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetplayService.java	Thu Jul 19 22:55:36 2012 +0200
@@ -27,7 +27,7 @@
 			netconn = new Netconn(getApplicationContext(), "AndroidTester");
 		} catch (IOException e) {
 			// TODO better handling
-			throw new RuntimeException("Unable to start frontlib");
+			throw new RuntimeException("Unable to start frontlib", e);
 		}
     	timer = new CountDownTimer(Long.MAX_VALUE, 50) {
 			@Override
@@ -46,6 +46,7 @@
 	
 	@Override
 	public void onDestroy() {
+		timer.cancel();
 		netconn.disconnect();
 		Flib.INSTANCE.flib_quit();
 	}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java	Thu Jul 19 22:55:36 2012 +0200
@@ -28,6 +28,12 @@
 	}
 
 	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		getActivity().unbindService(serviceConnection);
+	}
+	
+	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 		return inflater.inflate(R.layout.lobby_players_fragment, container, false);
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomList.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomList.java	Thu Jul 19 22:55:36 2012 +0200
@@ -9,7 +9,6 @@
 	
 	public void addRoomWithNewId(RoomPtr roomPtr) {
 		JnaFrontlib.Room r = roomPtr.deref();
-		Log.d("RoomList", "Adding room "+r.name);
 		long id = nextId++;
 		put(r.name, new Room(r.name, r.map, r.scheme, r.weapons, r.owner, r.playerCount, r.teamCount, r.inProgress, id));
 	}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java	Thu Jul 19 22:53:39 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java	Thu Jul 19 22:55:36 2012 +0200
@@ -48,18 +48,24 @@
 	}
 	
 	@Override
+	public void onResume() {
+		super.onResume();
+		if(netconn != null) {
+			netconn.sendRoomlistRequest();
+			autoRefreshTimer.start();
+		}
+	}
+	
+	@Override
 	public void onPause() {
 		super.onPause();
 		autoRefreshTimer.cancel();
 	}
 	
 	@Override
-	public void onResume() {
-		super.onResume();
-		if(netconn != null) {
-			netconn.sendRoomlistRequest();
-			autoRefreshTimer.start();
-		}
+	public void onDestroy() {
+		super.onDestroy();
+		getActivity().unbindService(serviceConnection);
 	}
 	
     private ServiceConnection serviceConnection = new ServiceConnection() {