Hedgeroid: Got the roomlist working... more or less.
authorMedo <smaxein@googlemail.com>
Thu, 19 Jul 2012 18:58:18 +0200
changeset 7342 0e29eec2df5c
parent 7340 62043f5f7c67
child 7344 25b8906f901a
Hedgeroid: Got the roomlist working... more or less.
project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_ingame.png
project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_preparing.png
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/res/values/strings.xml
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/JnaFrontlib.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netconn.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableLinkedHashMap.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Player.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerList.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerListAdapter.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/Room.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/RoomListAdapter.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TestActivity.java
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_ingame.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_preparing.png has changed
--- a/project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml	Thu Jul 19 18:58:18 2012 +0200
@@ -5,7 +5,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical" >
 
-    <ScrollView
+	<ScrollView
         android:id="@+id/lobbyConsoleScroll"
         android:layout_width="match_parent"
         android:layout_height="0dp"
@@ -23,7 +23,7 @@
             tools:context=".LobbyActivity" />
     </ScrollView>
 
-    <EditText
+	<EditText
         android:id="@+id/lobbyChatInput"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
--- a/project_files/Android-build/SDL-android-project/res/layout/lobby_players_fragment.xml	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_players_fragment.xml	Thu Jul 19 18:58:18 2012 +0200
@@ -12,6 +12,7 @@
         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 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/lobby_rooms_fragment.xml	Thu Jul 19 18:58:18 2012 +0200
@@ -12,7 +12,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:drawSelectorOnTop="false"
-        tools:listitem="@android:layout/simple_list_item_1" />
+        android:animateLayoutChanges="true"
+        tools:listitem="@android:layout/simple_list_item_2" />
 
     <TextView
         android:id="@id/android:empty"
--- a/project_files/Android-build/SDL-android-project/res/values/strings.xml	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml	Thu Jul 19 18:58:18 2012 +0200
@@ -66,6 +66,15 @@
     <!-- Player list -->
     <string name="no_players_in_list">No players</string>
     
+    <!-- Roomlist -->
+    <string name="roomlist_owner">by %1$s</string>
+    <string name="roomlist_map">map: %1$s</string>
+    <string name="roomlist_scheme">scheme: %1$s</string>
+    <string name="roomlist_weapons">weapons: %1$s</string>
+    <string name="map_regular">Random map</string>
+    <string name="map_maze">Random maze</string>
+    <string name="map_drawn">Drawn map</string>
+    
     <!-- Chatlog messages -->
     <string name="log_player_join">%1$s has joined.</string>
     <string name="log_player_leave">%1$s has left.</string>
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/JnaFrontlib.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/JnaFrontlib.java	Thu Jul 19 18:58:18 2012 +0200
@@ -2,6 +2,8 @@
 import java.nio.Buffer;
 import java.util.Collections;
 
+import android.util.Log;
+
 import com.sun.jna.Callback;
 import com.sun.jna.Library;
 import com.sun.jna.Native;
@@ -15,6 +17,25 @@
 		System.loadLibrary("SDL_net");
 	}
 	public static final JnaFrontlib INSTANCE = (JnaFrontlib)Native.loadLibrary("frontlib", JnaFrontlib.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, FrontlibTypeMapper.INSTANCE));
+	
+	// Hook frontlib logging into Android logging
+	private static final JnaFrontlib.LogCallback logCb = new JnaFrontlib.LogCallback() {
+		public void callback(int level, String message) {
+			if(level >= JnaFrontlib.FLIB_LOGLEVEL_ERROR) {
+				Log.e("Frontlib", message);
+			} else if(level == JnaFrontlib.FLIB_LOGLEVEL_WARNING){
+				Log.w("Frontlib", message);
+			} else if(level == JnaFrontlib.FLIB_LOGLEVEL_INFO){
+				Log.i("Frontlib", message);
+			} else if(level <= JnaFrontlib.FLIB_LOGLEVEL_DEBUG){
+				Log.d("Frontlib", message);
+			}
+		}
+	};
+	static {
+		INSTANCE.flib_log_setLevel(JnaFrontlib.FLIB_LOGLEVEL_WARNING);
+		INSTANCE.flib_log_setCallback(logCb);
+	}
 }
 
 public interface JnaFrontlib extends Library {
@@ -56,14 +77,60 @@
 	static class MapconnPtr extends PointerType { }
 	static class GameconnPtr extends PointerType { }
 	static class MetaschemePtr extends PointerType { }
-	static class RoomlistPtr extends PointerType { }
-	static class RoomPtr extends PointerType { }
+	
+	static class RoomArrayPtr extends PointerType { 
+		/**
+		 * Returns the (native-owned) rooms in this list
+		 */
+		public RoomPtr[] getRooms(int count) {
+			Pointer ptr = getPointer();
+			if(ptr == null) {
+				return new RoomPtr[0];
+			}
+			Pointer[] untypedPtrs = ptr.getPointerArray(0, count);
+			RoomPtr[] typedPtrs = new RoomPtr[count];
+			for(int i=0; i<count; i++) {
+				typedPtrs[i] = new RoomPtr(untypedPtrs[i]);
+			}
+			return typedPtrs;
+		}
+	}
+	
+	static class RoomPtr extends PointerType {
+		public RoomPtr() { super(); }
+		public RoomPtr(Pointer ptr) { super(ptr); }
+		
+		public Room deref() {
+			Room result = new Room(getPointer());
+			result.read();
+			return result;
+		}
+	}
+	
 	static class TeamPtr extends PointerType { }
 	static class WeaponsetPtr extends PointerType { }
 	static class MapRecipePtr extends PointerType { }
 	static class SchemePtr extends PointerType { }
 	static class GameSetupPtr extends PointerType { }
 	
+	static class Room extends Structure {
+		public static class byVal extends Room implements Structure.ByValue {}
+		public static class byRef extends Room implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"inProgress", "name", "playerCount", "teamCount", "owner", "map", "scheme", "weapons"};
+		
+		public Room() { super(); setFieldOrder(FIELD_ORDER); }
+		public Room(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+	    public boolean inProgress;
+	    public String name;
+	    public int playerCount;
+	    public int teamCount;
+	    public String owner;
+	    public String map;
+	    public String scheme;
+	    public String weapons;
+	}
+	
 	public static interface VoidCallback extends Callback {
 		void callback(Pointer context);
 	}
@@ -89,11 +156,15 @@
 	}
 	
 	public static interface RoomCallback extends Callback {
-		void callback(Pointer context, JnaFrontlib.RoomPtr arg1);
+		void callback(Pointer context, RoomPtr arg1);
+	}
+	
+	public static interface RoomListCallback extends Callback {
+		void callback(Pointer context, RoomArrayPtr arg1, int count);
 	}
 	
 	public static interface StrRoomCallback extends Callback {
-		void callback(Pointer context, String arg1, JnaFrontlib.RoomPtr arg2);
+		void callback(Pointer context, String arg1, RoomPtr arg2);
 	}
 	
 	public static interface BoolCallback extends Callback {
@@ -105,7 +176,7 @@
 	}
 	
 	public static interface TeamCallback extends Callback {
-		void callback(Pointer context, JnaFrontlib.TeamPtr arg1);
+		void callback(Pointer context, TeamPtr arg1);
 	}
 	
 	public static interface BytesCallback extends Callback {
@@ -117,133 +188,139 @@
 	}
 	
 	public static interface SchemeCallback extends Callback {
-		void callback(Pointer context, JnaFrontlib.SchemePtr arg1);
+		void callback(Pointer context, SchemePtr arg1);
 	}
 	
 	public static interface MapIntCallback extends Callback {
-		void callback(Pointer context, JnaFrontlib.MapRecipePtr arg1, int arg2);
+		void callback(Pointer context, MapRecipePtr arg1, int arg2);
 	}
 	
 	public static interface WeaponsetCallback extends Callback {
-		void callback(Pointer context, JnaFrontlib.WeaponsetPtr arg1);
+		void callback(Pointer context, WeaponsetPtr arg1);
 	}
 	
 	public static interface MapimageCallback extends Callback {
 		void callback(Pointer context, Pointer buffer, int hedgehogCount);
 	}
 	
+	public static interface LogCallback extends Callback {
+		void callback(int level, String logMessage);
+	}
+	
     int flib_init();
     void flib_quit();
 	
-	JnaFrontlib.NetconnPtr flib_netconn_create(String playerName, JnaFrontlib.MetaschemePtr meta, String dataDirPath, String host, int port);
-	void flib_netconn_destroy(JnaFrontlib.NetconnPtr conn);
+	NetconnPtr flib_netconn_create(String playerName, MetaschemePtr meta, String dataDirPath, String host, int port);
+	void flib_netconn_destroy(NetconnPtr conn);
 
-	void flib_netconn_tick(JnaFrontlib.NetconnPtr conn);
-	JnaFrontlib.RoomlistPtr flib_netconn_get_roomlist(JnaFrontlib.NetconnPtr conn);
-	boolean flib_netconn_is_chief(JnaFrontlib.NetconnPtr conn);
-	boolean flib_netconn_is_in_room_context(JnaFrontlib.NetconnPtr conn);
-	JnaFrontlib.GameSetupPtr flib_netconn_create_gamesetup(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_quit(JnaFrontlib.NetconnPtr conn, String quitmsg);
-	int flib_netconn_send_chat(JnaFrontlib.NetconnPtr conn, String chat);
-	int flib_netconn_send_teamchat(JnaFrontlib.NetconnPtr conn, String msg);
-	int flib_netconn_send_password(JnaFrontlib.NetconnPtr conn, String passwd);
-	int flib_netconn_send_nick(JnaFrontlib.NetconnPtr conn, String nick);
-	int flib_netconn_send_joinRoom(JnaFrontlib.NetconnPtr conn, String room);
-	int flib_netconn_send_createRoom(JnaFrontlib.NetconnPtr conn, String room);
-	int flib_netconn_send_renameRoom(JnaFrontlib.NetconnPtr conn, String roomName);
-	int flib_netconn_send_leaveRoom(JnaFrontlib.NetconnPtr conn, String msg);
-	int flib_netconn_send_toggleReady(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_addTeam(JnaFrontlib.NetconnPtr conn, JnaFrontlib.TeamPtr team);
-	int flib_netconn_send_removeTeam(JnaFrontlib.NetconnPtr conn, String teamname);
-	int flib_netconn_send_engineMessage(JnaFrontlib.NetconnPtr conn, Buffer message, NativeLong size); // TODO check if NativeLong==size_t
-	int flib_netconn_send_teamHogCount(JnaFrontlib.NetconnPtr conn, String teamname, int hogcount);
-	int flib_netconn_send_teamColor(JnaFrontlib.NetconnPtr conn, String teamname, int colorIndex);
-	int flib_netconn_send_weaponset(JnaFrontlib.NetconnPtr conn, JnaFrontlib.WeaponsetPtr weaponset);
-	int flib_netconn_send_map(JnaFrontlib.NetconnPtr conn, JnaFrontlib.MapRecipePtr map);
-	int flib_netconn_send_mapName(JnaFrontlib.NetconnPtr conn, String mapName);
-	int flib_netconn_send_mapGen(JnaFrontlib.NetconnPtr conn, int mapGen);
-	int flib_netconn_send_mapTemplate(JnaFrontlib.NetconnPtr conn, int templateFilter);
-	int flib_netconn_send_mapMazeSize(JnaFrontlib.NetconnPtr conn, int mazeSize);
-	int flib_netconn_send_mapSeed(JnaFrontlib.NetconnPtr conn, String seed);
-	int flib_netconn_send_mapTheme(JnaFrontlib.NetconnPtr conn, String theme);
-	int flib_netconn_send_mapDrawdata(JnaFrontlib.NetconnPtr conn, Buffer drawData, NativeLong size);
-	int flib_netconn_send_script(JnaFrontlib.NetconnPtr conn, String scriptName);
-	int flib_netconn_send_scheme(JnaFrontlib.NetconnPtr conn, JnaFrontlib.SchemePtr scheme);
-	int flib_netconn_send_roundfinished(JnaFrontlib.NetconnPtr conn, boolean withoutError);
-	int flib_netconn_send_ban(JnaFrontlib.NetconnPtr conn, String playerName);
-	int flib_netconn_send_kick(JnaFrontlib.NetconnPtr conn, String playerName);
-	int flib_netconn_send_playerInfo(JnaFrontlib.NetconnPtr conn, String playerName);
-	int flib_netconn_send_playerFollow(JnaFrontlib.NetconnPtr conn, String playerName);
-	int flib_netconn_send_startGame(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_toggleRestrictJoins(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_toggleRestrictTeams(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_clearAccountsCache(JnaFrontlib.NetconnPtr conn);
-	int flib_netconn_send_setServerVar(JnaFrontlib.NetconnPtr conn, String name, String value);
-	int flib_netconn_send_getServerVars(JnaFrontlib.NetconnPtr conn);
+	void flib_netconn_tick(NetconnPtr conn);
+	boolean flib_netconn_is_chief(NetconnPtr conn);
+	boolean flib_netconn_is_in_room_context(NetconnPtr conn);
+	String flib_netconn_get_playername(NetconnPtr conn);
+	GameSetupPtr flib_netconn_create_gamesetup(NetconnPtr conn);
+	int flib_netconn_send_quit(NetconnPtr conn, String quitmsg);
+	int flib_netconn_send_chat(NetconnPtr conn, String chat);
+	int flib_netconn_send_teamchat(NetconnPtr conn, String msg);
+	int flib_netconn_send_password(NetconnPtr conn, String passwd);
+	int flib_netconn_send_nick(NetconnPtr conn, String nick);
+	int flib_netconn_send_request_roomlist(NetconnPtr conn);
+	int flib_netconn_send_joinRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_createRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_renameRoom(NetconnPtr conn, String roomName);
+	int flib_netconn_send_leaveRoom(NetconnPtr conn, String msg);
+	int flib_netconn_send_toggleReady(NetconnPtr conn);
+	int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team);
+	int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname);
+	int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size); // TODO check if NativeLong==size_t
+	int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount);
+	int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex);
+	int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset);
+	int flib_netconn_send_map(NetconnPtr conn, MapRecipePtr map);
+	int flib_netconn_send_mapName(NetconnPtr conn, String mapName);
+	int flib_netconn_send_mapGen(NetconnPtr conn, int mapGen);
+	int flib_netconn_send_mapTemplate(NetconnPtr conn, int templateFilter);
+	int flib_netconn_send_mapMazeSize(NetconnPtr conn, int mazeSize);
+	int flib_netconn_send_mapSeed(NetconnPtr conn, String seed);
+	int flib_netconn_send_mapTheme(NetconnPtr conn, String theme);
+	int flib_netconn_send_mapDrawdata(NetconnPtr conn, Buffer drawData, NativeLong size);
+	int flib_netconn_send_script(NetconnPtr conn, String scriptName);
+	int flib_netconn_send_scheme(NetconnPtr conn, SchemePtr scheme);
+	int flib_netconn_send_roundfinished(NetconnPtr conn, boolean withoutError);
+	int flib_netconn_send_ban(NetconnPtr conn, String playerName);
+	int flib_netconn_send_kick(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerInfo(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerFollow(NetconnPtr conn, String playerName);
+	int flib_netconn_send_startGame(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictJoins(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictTeams(NetconnPtr conn);
+	int flib_netconn_send_clearAccountsCache(NetconnPtr conn);
+	int flib_netconn_send_setServerVar(NetconnPtr conn, String name, String value);
+	int flib_netconn_send_getServerVars(NetconnPtr conn);
 	
-	void flib_netconn_onMessage(JnaFrontlib.NetconnPtr conn, JnaFrontlib.IntStrCallback callback, Pointer context);
-	void flib_netconn_onChat(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrStrCallback callback, Pointer context);
-	void flib_netconn_onConnected(JnaFrontlib.NetconnPtr conn, JnaFrontlib.VoidCallback callback, Pointer context);
-	void flib_netconn_onDisconnected(JnaFrontlib.NetconnPtr conn, JnaFrontlib.IntStrCallback callback, Pointer context);
-	void flib_netconn_onRoomAdd(JnaFrontlib.NetconnPtr conn, JnaFrontlib.RoomCallback callback, Pointer context);
-	void flib_netconn_onRoomDelete(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onRoomUpdate(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrRoomCallback callback, Pointer context);
-	void flib_netconn_onLobbyJoin(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onLobbyLeave(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrStrCallback callback, Pointer context);
-	void flib_netconn_onNickTaken(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onPasswordRequest(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onEnterRoom(JnaFrontlib.NetconnPtr conn, JnaFrontlib.BoolCallback callback, Pointer context);
-	void flib_netconn_onRoomChiefStatus(JnaFrontlib.NetconnPtr conn, JnaFrontlib.BoolCallback callback, Pointer context);
-	void flib_netconn_onReadyState(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrBoolCallback callback, Pointer context);
-	void flib_netconn_onLeaveRoom(JnaFrontlib.NetconnPtr conn, JnaFrontlib.IntStrCallback callback, Pointer context);
-	void flib_netconn_onTeamAdd(JnaFrontlib.NetconnPtr conn, JnaFrontlib.TeamCallback callback, Pointer context);
-	void flib_netconn_onTeamDelete(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onRoomJoin(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onRoomLeave(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrStrCallback callback, Pointer context);
-	void flib_netconn_onRunGame(JnaFrontlib.NetconnPtr conn, JnaFrontlib.VoidCallback callback, Pointer context);
-	void flib_netconn_onTeamAccepted(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onHogCountChanged(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrIntCallback callback, Pointer context);
-	void flib_netconn_onTeamColorChanged(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrIntCallback callback, Pointer context);
-	void flib_netconn_onEngineMessage(JnaFrontlib.NetconnPtr conn, JnaFrontlib.BytesCallback callback, Pointer context);
-	void flib_netconn_onCfgScheme(JnaFrontlib.NetconnPtr conn, JnaFrontlib.SchemeCallback callback, Pointer context);
-	void flib_netconn_onMapChanged(JnaFrontlib.NetconnPtr conn, JnaFrontlib.MapIntCallback callback, Pointer context);
-	void flib_netconn_onScriptChanged(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_netconn_onWeaponsetChanged(JnaFrontlib.NetconnPtr conn, JnaFrontlib.WeaponsetCallback callback, Pointer context);
-	void flib_netconn_onAdminAccess(JnaFrontlib.NetconnPtr conn, JnaFrontlib.VoidCallback callback, Pointer context);
-	void flib_netconn_onServerVar(JnaFrontlib.NetconnPtr conn, JnaFrontlib.StrStrCallback callback, Pointer context);
+	void flib_netconn_onMessage(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onChat(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onConnected(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onDisconnected(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onRoomlist(NetconnPtr conn, RoomListCallback callback, Pointer context);
+	void flib_netconn_onRoomAdd(NetconnPtr conn, RoomCallback callback, Pointer context);
+	void flib_netconn_onRoomDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomUpdate(NetconnPtr conn, StrRoomCallback callback, Pointer context);
+	void flib_netconn_onLobbyJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onLobbyLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onNickTaken(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onPasswordRequest(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onEnterRoom(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onRoomChiefStatus(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onReadyState(NetconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_netconn_onLeaveRoom(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onTeamAdd(NetconnPtr conn, TeamCallback callback, Pointer context);
+	void flib_netconn_onTeamDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onRunGame(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onTeamAccepted(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onHogCountChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onTeamColorChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onEngineMessage(NetconnPtr conn, BytesCallback callback, Pointer context);
+	void flib_netconn_onCfgScheme(NetconnPtr conn, SchemeCallback callback, Pointer context);
+	void flib_netconn_onMapChanged(NetconnPtr conn, MapIntCallback callback, Pointer context);
+	void flib_netconn_onScriptChanged(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onWeaponsetChanged(NetconnPtr conn, WeaponsetCallback callback, Pointer context);
+	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
 
 	// Gameconn
-	JnaFrontlib.GameconnPtr flib_gameconn_create(String playerName, JnaFrontlib.GameSetupPtr setup, boolean netgame);
-	JnaFrontlib.GameconnPtr flib_gameconn_create_playdemo(Buffer demo, NativeLong size);
-	JnaFrontlib.GameconnPtr flib_gameconn_create_loadgame(String playerName, Buffer save, NativeLong size);
-	JnaFrontlib.GameconnPtr flib_gameconn_create_campaign(String playerName, String seed, String script);
+	GameconnPtr flib_gameconn_create(String playerName, GameSetupPtr setup, boolean netgame);
+	GameconnPtr flib_gameconn_create_playdemo(Buffer demo, NativeLong size);
+	GameconnPtr flib_gameconn_create_loadgame(String playerName, Buffer save, NativeLong size);
+	GameconnPtr flib_gameconn_create_campaign(String playerName, String seed, String script);
 
-	void flib_gameconn_destroy(JnaFrontlib.GameconnPtr conn);
-	int flib_gameconn_getport(JnaFrontlib.GameconnPtr conn);
-	void flib_gameconn_tick(JnaFrontlib.GameconnPtr conn);
+	void flib_gameconn_destroy(GameconnPtr conn);
+	int flib_gameconn_getport(GameconnPtr conn);
+	void flib_gameconn_tick(GameconnPtr conn);
 
-	int flib_gameconn_send_enginemsg(JnaFrontlib.GameconnPtr conn, Buffer data, NativeLong len);
-	int flib_gameconn_send_textmsg(JnaFrontlib.GameconnPtr conn, int msgtype, String msg);
-	int flib_gameconn_send_chatmsg(JnaFrontlib.GameconnPtr conn, String playername, String msg);
+	int flib_gameconn_send_enginemsg(GameconnPtr conn, Buffer data, NativeLong len);
+	int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg);
+	int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg);
 	
-	void flib_gameconn_onConnect(JnaFrontlib.GameconnPtr conn, JnaFrontlib.VoidCallback callback, Pointer context);
-	void flib_gameconn_onDisconnect(JnaFrontlib.GameconnPtr conn, JnaFrontlib.IntCallback callback, Pointer context);
-	void flib_gameconn_onErrorMessage(JnaFrontlib.GameconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_gameconn_onChat(JnaFrontlib.GameconnPtr conn, JnaFrontlib.StrBoolCallback callback, Pointer context);
-	void flib_gameconn_onGameRecorded(JnaFrontlib.GameconnPtr conn, JnaFrontlib.BytesBoolCallback callback, Pointer context);
-	void flib_gameconn_onEngineMessage(JnaFrontlib.GameconnPtr conn, JnaFrontlib.BytesCallback callback, Pointer context);
+	void flib_gameconn_onConnect(GameconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_gameconn_onDisconnect(GameconnPtr conn, IntCallback callback, Pointer context);
+	void flib_gameconn_onErrorMessage(GameconnPtr conn, StrCallback callback, Pointer context);
+	void flib_gameconn_onChat(GameconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_gameconn_onGameRecorded(GameconnPtr conn, BytesBoolCallback callback, Pointer context);
+	void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context);
 	
 	// MapConn
-	JnaFrontlib.MapconnPtr flib_mapconn_create(JnaFrontlib.MapRecipePtr mapdesc);
-	void flib_mapconn_destroy(JnaFrontlib.MapconnPtr conn);
-	int flib_mapconn_getport(JnaFrontlib.MapconnPtr conn);
-	void flib_mapconn_onSuccess(JnaFrontlib.MapconnPtr conn, JnaFrontlib.MapimageCallback callback, Pointer context);
-	void flib_mapconn_onFailure(JnaFrontlib.MapconnPtr conn, JnaFrontlib.StrCallback callback, Pointer context);
-	void flib_mapconn_tick(JnaFrontlib.MapconnPtr conn);
+	MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc);
+	void flib_mapconn_destroy(MapconnPtr conn);
+	int flib_mapconn_getport(MapconnPtr conn);
+	void flib_mapconn_onSuccess(MapconnPtr conn, MapimageCallback callback, Pointer context);
+	void flib_mapconn_onFailure(MapconnPtr conn, StrCallback callback, Pointer context);
+	void flib_mapconn_tick(MapconnPtr conn);
 	
 	// GameSetup
-	void flib_gamesetup_destroy(JnaFrontlib.GameSetupPtr gamesetup);
-	JnaFrontlib.GameSetupPtr flib_gamesetup_copy(JnaFrontlib.GameSetupPtr gamesetup);
+	void flib_gamesetup_destroy(GameSetupPtr gamesetup);
+	GameSetupPtr flib_gamesetup_copy(GameSetupPtr gamesetup);
 	
 	// MapRecipe
 	public static final int MAPGEN_REGULAR = 0;
@@ -265,18 +342,26 @@
 	public static final int MAZE_SIZE_MEDIUM_ISLANDS = 4;
 	public static final int MAZE_SIZE_LARGE_ISLANDS = 5;
 	
-	JnaFrontlib.MapRecipePtr flib_map_create_regular(String seed, String theme, int templateFilter);
-	JnaFrontlib.MapRecipePtr flib_map_create_maze(String seed, String theme, int mazeSize);
-	JnaFrontlib.MapRecipePtr flib_map_create_named(String seed, String name);
-	JnaFrontlib.MapRecipePtr flib_map_create_drawn(String seed, String theme, Buffer drawData, NativeLong drawDataSize);
-	JnaFrontlib.MapRecipePtr flib_map_copy(JnaFrontlib.MapRecipePtr map);
-	JnaFrontlib.MapRecipePtr flib_map_retain(JnaFrontlib.MapRecipePtr map);
-	void flib_map_release(JnaFrontlib.MapRecipePtr map);
+	MapRecipePtr flib_map_create_regular(String seed, String theme, int templateFilter);
+	MapRecipePtr flib_map_create_maze(String seed, String theme, int mazeSize);
+	MapRecipePtr flib_map_create_named(String seed, String name);
+	MapRecipePtr flib_map_create_drawn(String seed, String theme, Buffer drawData, NativeLong drawDataSize);
+	MapRecipePtr flib_map_copy(MapRecipePtr map);
+	MapRecipePtr flib_map_retain(MapRecipePtr map);
+	void flib_map_release(MapRecipePtr map);
 	
 	// Metascheme
-	JnaFrontlib.MetaschemePtr flib_metascheme_from_ini(String filename);
-	JnaFrontlib.MetaschemePtr flib_metascheme_retain(JnaFrontlib.MetaschemePtr metainfo);
-	void flib_metascheme_release(JnaFrontlib.MetaschemePtr metainfo);
+	MetaschemePtr flib_metascheme_from_ini(String filename);
+	MetaschemePtr flib_metascheme_retain(MetaschemePtr metainfo);
+	void flib_metascheme_release(MetaschemePtr metainfo);
+	
+	public static final int FLIB_LOGLEVEL_ALL = -100;
+	public static final int FLIB_LOGLEVEL_DEBUG = -1;
+	public static final int FLIB_LOGLEVEL_INFO = 0;
+	public static final int FLIB_LOGLEVEL_WARNING = 1;
+	public static final int FLIB_LOGLEVEL_ERROR = 2;
+	public static final int FLIB_LOGLEVEL_NONE = 100;
 	
     void flib_log_setLevel(int level);
+    void flib_log_setCallback(LogCallback callback);
 }
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netconn.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netconn.java	Thu Jul 19 18:58:18 2012 +0200
@@ -7,8 +7,14 @@
 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.IntStrCallback;
 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.MetaschemePtr;
 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.NetconnPtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomArrayPtr;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomListCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr;
 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrRoomCallback;
 import org.hedgewars.hedgeroid.netplay.JnaFrontlib.StrStrCallback;
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.VoidCallback;
 
 import com.sun.jna.Pointer;
 
@@ -29,19 +35,20 @@
 	private String playerName;
 	
 	public final PlayerList playerList = new PlayerList();
+	public final RoomList roomList = new RoomList();
 	public final MessageLog lobbyLog;
 	public final MessageLog roomLog;
 	
 	private StrCallback lobbyJoinCb = new StrCallback() {
 		public void callback(Pointer context, String arg1) {
-			playerList.addPlayer(arg1);
+			playerList.addPlayerWithNewId(arg1);
 			lobbyLog.appendPlayerJoin(arg1);
 		}
 	};
 	
 	private StrStrCallback lobbyLeaveCb = new StrStrCallback() {
 		public void callback(Pointer context, String name, String msg) {
-			playerList.removePlayer(name);
+			playerList.remove(name);
 			lobbyLog.appendPlayerLeave(name, msg);
 		}
 	};
@@ -58,6 +65,40 @@
 		}
 	};
 	
+	private RoomCallback roomAddCb = new RoomCallback() {
+		public void callback(Pointer context, RoomPtr roomPtr) {
+			roomList.addRoomWithNewId(roomPtr);
+		}
+	};
+	
+	private StrRoomCallback roomUpdateCb = new StrRoomCallback() {
+		public void callback(Pointer context, String name, RoomPtr roomPtr) {
+			roomList.updateRoom(name, roomPtr);
+		}
+	};
+	
+	private StrCallback roomDeleteCb = new StrCallback() {
+		public void callback(Pointer context, String name) {
+			roomList.remove(name);
+		}
+	};
+	
+	private VoidCallback connectedCb = new VoidCallback() {
+		public void callback(Pointer context) {
+			// TODO I guess more needs to happen here...
+			FLIB.flib_netconn_send_request_roomlist(conn);
+		}
+	};
+	
+	private RoomListCallback roomlistCb = new RoomListCallback() {
+		public void callback(Pointer context, RoomArrayPtr arg1, int count) {
+			roomList.clear();
+			for(RoomPtr roomPtr : arg1.getRooms(count)) {
+				roomList.addRoomWithNewId(roomPtr);
+			}
+		}
+	};
+	
 	/**
 	 * Connect to the official Hedgewars server.
 	 * 
@@ -96,6 +137,11 @@
 			FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null);
 			FLIB.flib_netconn_onChat(conn, chatCb, null);
 			FLIB.flib_netconn_onMessage(conn, messageCb, null);
+			FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null);
+			FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null);
+			FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null);
+			FLIB.flib_netconn_onConnected(conn, connectedCb, null);
+			FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
 		} finally {
 			FLIB.flib_metascheme_release(meta);
 		}
@@ -133,6 +179,7 @@
 	public void sendNick(String nick) { FLIB.flib_netconn_send_nick(conn, nick); }
 	public void sendPassword(String password) { FLIB.flib_netconn_send_password(conn, password); }
 	public void sendQuit(String message) { FLIB.flib_netconn_send_quit(conn, message); }
+	public void sendRoomlistRequest() { FLIB.flib_netconn_send_request_roomlist(conn); }
 	
 	public boolean isConnected() {
 		return conn != null;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ObservableLinkedHashMap.java	Thu Jul 19 18:58:18 2012 +0200
@@ -0,0 +1,72 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A map of items, sorted by time of insertion (earliest first). 
+ * Observers can be notified about insertions, deletions and changes (which don't change the order).
+ * This is useful for e.g. the lists of current rooms and players, because it allows easy addition
+ * and removal of entries on the one side, as well as reaction to these events by UI elements.
+ */
+public class ObservableLinkedHashMap<K,V> {
+	private LinkedHashMap<K,V> map = new LinkedHashMap<K,V>();
+	private List<Observer<K,V>> observers = new LinkedList<Observer<K,V>>();
+	
+	public Collection<V> getValues() {
+		return Collections.unmodifiableCollection(map.values());
+	}
+	
+	public Map<K,V> getMap() {
+		return Collections.unmodifiableMap(map);
+	}
+	
+	public void observe(Observer<K,V> observer) {
+		observers.add(observer);
+	}
+	
+	public void unobserve(Observer<K,V> observer) {
+		observers.remove(observer);
+	}
+	
+	// TODO ugh
+	public void clear() {
+		while(!map.isEmpty()) {
+			remove(map.keySet().iterator().next());
+		}
+	}
+	
+	public void put(K key, V value) {
+		V oldValue = map.put(key, value);
+		Map<K,V> unmodifiableMap = Collections.unmodifiableMap(map);
+		if(oldValue != null) {
+			for(Observer<K,V> o : observers) {
+				o.itemReplaced(unmodifiableMap, key, oldValue, value);
+			}
+		} else {
+			for(Observer<K,V> o : observers) {
+				o.itemAdded(unmodifiableMap, key, value);
+			}
+		}
+	}
+	
+	public void remove(K key) {
+		V oldValue = map.remove(key);
+		if(oldValue != null) {
+			Map<K,V> unmodifiableMap = Collections.unmodifiableMap(map);
+			for(Observer<K,V> o : observers) {
+				o.itemRemoved(unmodifiableMap, key, oldValue);
+			}
+		}
+	}
+
+	public static interface Observer<K,V> {
+		void itemAdded(Map<K,V> map, K key, V value);
+		void itemRemoved(Map<K,V> map, K key, V oldValue);
+		void itemReplaced(Map<K,V> map, K key, V oldValue, V newValue);
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Player.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Player.java	Thu Jul 19 18:58:18 2012 +0200
@@ -6,11 +6,11 @@
 	public static final ByNameComparator nameComparator = new ByNameComparator(); 
 
 	public final String name;
-	public final long playerId;
+	public final long id; // for ListView
 	
-	public Player(String name, long playerId) {
+	public Player(String name, long id) {
 		this.name = name;
-		this.playerId = playerId;
+		this.id = id;
 	}
 	
 	private static class ByNameComparator implements Comparator<Player> {
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerList.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerList.java	Thu Jul 19 18:58:18 2012 +0200
@@ -1,51 +1,12 @@
 package org.hedgewars.hedgeroid.netplay;
 
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-public class PlayerList {
-	private List<Player> list = new LinkedList<Player>();
-	private List<Observer> observers = new LinkedList<Observer>();
+public class PlayerList extends ObservableLinkedHashMap<String, Player> {
 	private long nextId = 1;
 	
-	public List<Player> getList() {
-		return Collections.unmodifiableList(list);
-	}
-	
-	public void observePlayerList(Observer plo) {
-		observers.add(plo);
-	}
-	
-	public void unobservePlayerList(Observer plo) {
-		observers.remove(plo);
+	public void addPlayerWithNewId(String name) {
+		Player p = new Player(name, nextId++);
+		put(name, p);
 	}
 	
-	void addPlayer(String name) {
-		Player p = new Player(name, nextId++);
-		list.add(p);
-		List<Player> unmodifiableList = Collections.unmodifiableList(list);
-		for(Observer o : observers) {
-			o.itemAdded(unmodifiableList, p);
-		}
-	}
-	
-	void removePlayer(String name) {
-		for(Iterator<Player> iter = list.iterator(); iter.hasNext();) {
-			Player p = iter.next();
-			if(name.equals(p.name)) {
-				iter.remove();
-				List<Player> unmodifiableList = Collections.unmodifiableList(list);
-				for(Observer o : observers) {
-					o.itemDeleted(unmodifiableList, p);
-				}
-			}
-		}
-	}
-
-	public static interface Observer {
-		void itemAdded(List<Player> newList, Player added);
-		void itemDeleted(List<Player> newList, Player deleted);
-	}
+	public interface Observer extends ObservableLinkedHashMap.Observer<String, Player> {}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerListAdapter.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerListAdapter.java	Thu Jul 19 18:58:18 2012 +0200
@@ -4,6 +4,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import org.hedgewars.hedgeroid.R;
 import org.hedgewars.hedgeroid.netplay.PlayerList.Observer;
@@ -32,19 +33,24 @@
 	}
 
 	public long getItemId(int position) {
-		return players.get(position).playerId;
+		return players.get(position).id;
 	}
 
 	public boolean hasStableIds() {
 		return true;
 	}
 
-	public void itemAdded(List<Player> newList, Player added) {
-		setPlayerList(newList);
+	public void itemAdded(Map<String, Player> map, String key, Player value) {
+		setPlayerList(map.values());
 	}
-	
-	public void itemDeleted(List<Player> newList, Player deleted) {
-		setPlayerList(newList);
+
+	public void itemRemoved(Map<String, Player> map, String key, Player oldValue) {
+		setPlayerList(map.values());
+	}
+
+	public void itemReplaced(Map<String, Player> map, String key,
+			Player oldValue, Player newValue) {
+		setPlayerList(map.values());
 	}
 	
 	public void setPlayerList(Collection<Player> players) {
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java	Thu Jul 19 18:58:18 2012 +0200
@@ -1,9 +1,5 @@
 package org.hedgewars.hedgeroid.netplay;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
 import org.hedgewars.hedgeroid.R;
 import org.hedgewars.hedgeroid.netplay.NetplayService.NetplayBinder;
 
@@ -14,7 +10,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.v4.app.ListFragment;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,13 +36,13 @@
     private ServiceConnection serviceConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder binder) {
         	netconn = ((NetplayBinder) binder).getNetconn();
-        	playerListAdapter.setPlayerList(netconn.playerList.getList());
-        	netconn.playerList.observePlayerList(playerListAdapter);
+        	playerListAdapter.setPlayerList(netconn.playerList.getValues());
+        	netconn.playerList.observe(playerListAdapter);
         }
 
         public void onServiceDisconnected(ComponentName className) {
         	// TODO navigate away
-        	netconn.playerList.unobservePlayerList(playerListAdapter);
+        	netconn.playerList.unobserve(playerListAdapter);
         	netconn = null;
         }
     };
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Room.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Room.java	Thu Jul 19 18:58:18 2012 +0200
@@ -1,9 +1,42 @@
 package org.hedgewars.hedgeroid.netplay;
 
-import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr;
-/*
+import org.hedgewars.hedgeroid.R;
+
+import android.content.res.Resources;
+
 public class Room {
+	public static final String MAP_REGULAR = "+rnd+";
+	public static final String MAP_MAZE = "+maze+";
+	public static final String MAP_DRAWN = "+drawn+";
+	
 	public final String name, map, scheme, weapons, owner;
-	public final int players, clans;
+	public final int playerCount, teamCount;
 	public final boolean inProgress;
-}*/
+	public final long id;	// for ListView
+	
+	public Room(String name, String map, String scheme, String weapons,
+			String owner, int playerCount, int teamCount, boolean inProgress, long id) {
+		this.name = name;
+		this.map = map;
+		this.scheme = scheme;
+		this.weapons = weapons;
+		this.owner = owner;
+		this.playerCount = playerCount;
+		this.teamCount = teamCount;
+		this.inProgress = inProgress;
+		this.id = id;
+	}
+
+	public static String formatMapName(Resources res, String map) {
+		if(map.charAt(0)=='+') {
+			if(map.equals(MAP_REGULAR)) {
+				return res.getString(R.string.map_regular);
+			} else if(map.equals(MAP_MAZE)) {
+				return res.getString(R.string.map_maze);
+			} else if(map.equals(MAP_DRAWN)) {
+				return res.getString(R.string.map_drawn);
+			}
+		}
+		return map;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomList.java	Thu Jul 19 18:58:18 2012 +0200
@@ -0,0 +1,35 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import org.hedgewars.hedgeroid.netplay.JnaFrontlib.RoomPtr;
+
+import android.util.Log;
+
+public class RoomList extends ObservableLinkedHashMap<String, Room> {
+	private long nextId = 1;
+	
+	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));
+	}
+	
+	public void updateRoom(String name, RoomPtr roomPtr) {
+		Room oldEntry = getMap().get(name);
+		if(oldEntry == null) {
+			Log.e("RoomList", "Received update for unknown room: "+name);
+		} else {
+			JnaFrontlib.Room r = roomPtr.deref();
+			/*
+			 *  TODO Room renames are handled as re-insertions which push the room
+			 *  up to the top of the list again. Should maybe be revisited (sorting by ID is an option)
+			 */
+			if(!r.name.equals(oldEntry.name)) {
+				remove(oldEntry.name);
+			}
+			put(r.name, new Room(r.name, r.map, r.scheme, r.weapons, r.owner, r.playerCount, r.teamCount, r.inProgress, oldEntry.id));
+		}
+	}
+
+	public static interface Observer extends ObservableLinkedHashMap.Observer<String, Room> { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java	Thu Jul 19 18:58:18 2012 +0200
@@ -0,0 +1,101 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.netplay.RoomList.Observer;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.AlignmentSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class RoomListAdapter extends BaseAdapter implements Observer {
+	private List<Room> rooms = new ArrayList<Room>();
+	private Context context;
+	
+	public RoomListAdapter(Context context) {
+		this.context = context;
+	}
+	
+	public int getCount() {
+		return rooms.size();
+	}
+
+	public Object getItem(int position) {
+		return rooms.get(position);
+	}
+
+	public long getItemId(int position) {
+		return rooms.get(position).id;
+	}
+
+	public boolean hasStableIds() {
+		return true;
+	}
+
+	public void setList(Collection<Room> rooms) {
+		this.rooms = new ArrayList<Room>(rooms);
+		Collections.reverse(this.rooms); // We want to show the newest rooms first
+		notifyDataSetChanged();
+	}
+	
+	private static Spanned formatExtra(Resources res, Room room) {
+		String ownermsg = res.getString(R.string.roomlist_owner, room.owner);
+		String mapmsg = res.getString(R.string.roomlist_map, Room.formatMapName(res, room.map));
+		String schememsg = res.getString(R.string.roomlist_scheme, room.scheme);
+		String weaponsmsg = res.getString(R.string.roomlist_weapons,  room.weapons);
+		SpannableStringBuilder ssb = new SpannableStringBuilder();
+		ssb.append(ownermsg).append(" ").append(mapmsg).append("\n").append(schememsg).append(" ").append(weaponsmsg);
+		
+		int weaponOffset = ownermsg.length()+1+mapmsg.length()+1+schememsg.length()+1;
+		ssb.setSpan(new AlignmentSpan.Standard(Alignment.ALIGN_OPPOSITE), ownermsg.length(), ownermsg.length()+mapmsg.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		ssb.setSpan(new AlignmentSpan.Standard(Alignment.ALIGN_OPPOSITE), weaponOffset, weaponOffset+weaponsmsg.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		return ssb;
+	}
+	
+	public View getView(int position, View convertView, ViewGroup parent) {
+		View v = convertView;
+		TextView tv1;
+		if (v == null) {
+			LayoutInflater vi = LayoutInflater.from(context);
+			v = vi.inflate(android.R.layout.simple_list_item_2, null);
+			tv1 = (TextView)v.findViewById(android.R.id.text1);
+			tv1.setCompoundDrawablePadding(5);
+		} else {
+			tv1 = (TextView)v.findViewById(android.R.id.text1);
+		}
+		
+		Room room = rooms.get(position);
+		int iconRes = room.inProgress ? R.drawable.roomlist_ingame : R.drawable.roomlist_preparing;
+		TextView tv2 = (TextView)v.findViewById(android.R.id.text2);
+		tv1.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
+		tv1.setText(room.name);
+		tv2.setText(formatExtra(context.getResources(), room));
+		return v;
+	}
+
+	public void itemAdded(Map<String, Room> map, String key, Room value) {
+		setList(map.values());
+	}
+
+	public void itemRemoved(Map<String, Room> map, String key, Room oldValue) {
+		setList(map.values());
+	}
+
+	public void itemReplaced(Map<String, Room> map, String key, Room oldValue,
+			Room newValue) {
+		setList(map.values());
+	}
+}
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java	Thu Jul 19 18:58:18 2012 +0200
@@ -1,23 +1,44 @@
 package org.hedgewars.hedgeroid.netplay;
 
 import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.netplay.NetplayService.NetplayBinder;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.IBinder;
 import android.support.v4.app.ListFragment;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 public class RoomlistFragment extends ListFragment {
-	//List<Player> roomList;
+	private static final int AUTO_REFRESH_INTERVAL_MS = 10000;
+	
+	private Netconn netconn;
+	private RoomListAdapter adapter;
+	private CountDownTimer autoRefreshTimer = new CountDownTimer(Long.MAX_VALUE, AUTO_REFRESH_INTERVAL_MS) {
+		@Override
+		public void onTick(long millisUntilFinished) {
+			if(netconn != null && netconn.isConnected()) {
+				netconn.sendRoomlistRequest();
+			}
+		}
+		
+		@Override
+		public void onFinish() { }
+	};
 
 	@Override
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
-		/*playerList = new ArrayList<Player>();
-		PlayerListAdapter playerListAdapter = new PlayerListAdapter(getActivity());
-		playerListAdapter.setPlayerList(playerList);
-		setListAdapter(playerListAdapter);*/
+		getActivity().bindService(new Intent(getActivity(), NetplayService.class), serviceConnection,
+	            Context.BIND_AUTO_CREATE);
+		adapter = new RoomListAdapter(getActivity());
+		setListAdapter(adapter);
 	}
 
 	@Override
@@ -25,4 +46,33 @@
 			Bundle savedInstanceState) {
 		return inflater.inflate(R.layout.lobby_rooms_fragment, container, false);
 	}
+	
+	@Override
+	public void onPause() {
+		super.onPause();
+		autoRefreshTimer.cancel();
+	}
+	
+	@Override
+	public void onResume() {
+		super.onResume();
+		if(netconn != null) {
+			netconn.sendRoomlistRequest();
+			autoRefreshTimer.start();
+		}
+	}
+	
+    private ServiceConnection serviceConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder binder) {
+        	netconn = ((NetplayBinder) binder).getNetconn();
+        	adapter.setList(netconn.roomList.getValues());
+        	netconn.roomList.observe(adapter);
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+        	// TODO navigate away
+        	netconn.roomList.unobserve(adapter);
+        	netconn = null;
+        }
+    };
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TestActivity.java	Thu Jul 19 18:31:58 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TestActivity.java	Thu Jul 19 18:58:18 2012 +0200
@@ -13,12 +13,12 @@
 	@Override
 	protected void onCreate(Bundle arg0) {
 		super.onCreate(arg0);
-		setContentView(R.layout.activity_lobby);
-		/*ViewPager pager = (ViewPager)findViewById(R.id.pager);
-		pager.setAdapter(new Adapter(getSupportFragmentManager()));*/
+		setContentView(R.layout.activity_lobby_paged);
+		ViewPager pager = (ViewPager)findViewById(R.id.pager);
+		pager.setAdapter(new Adapter(getSupportFragmentManager()));
 	}
 	
-	/*private static class Adapter extends FragmentPagerAdapter {
+	private static class Adapter extends FragmentPagerAdapter {
 		public Adapter(FragmentManager mgr) {
 			super(mgr);
 		}
@@ -37,5 +37,5 @@
 			default: throw new IndexOutOfBoundsException();
 			}
 		}
-	}*/
+	}
 }