# HG changeset patch # User Medo # Date 1342717098 -7200 # Node ID 0e29eec2df5c3b1b80bcfa697948cdd37f5c2de9 # Parent 62043f5f7c674812c7551baa9ad4e4c3c78b4d0e Hedgeroid: Got the roomlist working... more or less. diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_ingame.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_ingame.png has changed diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_preparing.png Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_preparing.png has changed diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/res/layout/lobby_chat_fragment.xml --- 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" > - - + android:animateLayoutChanges="true" + tools:listitem="@android:layout/simple_list_item_2" /> No players + + by %1$s + map: %1$s + scheme: %1$s + weapons: %1$s + Random map + Random maze + Drawn map + %1$s has joined. %1$s has left. diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/JnaFrontlib.java --- 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 { + private LinkedHashMap map = new LinkedHashMap(); + private List> observers = new LinkedList>(); + + public Collection getValues() { + return Collections.unmodifiableCollection(map.values()); + } + + public Map getMap() { + return Collections.unmodifiableMap(map); + } + + public void observe(Observer observer) { + observers.add(observer); + } + + public void unobserve(Observer 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 unmodifiableMap = Collections.unmodifiableMap(map); + if(oldValue != null) { + for(Observer o : observers) { + o.itemReplaced(unmodifiableMap, key, oldValue, value); + } + } else { + for(Observer o : observers) { + o.itemAdded(unmodifiableMap, key, value); + } + } + } + + public void remove(K key) { + V oldValue = map.remove(key); + if(oldValue != null) { + Map unmodifiableMap = Collections.unmodifiableMap(map); + for(Observer o : observers) { + o.itemRemoved(unmodifiableMap, key, oldValue); + } + } + } + + public static interface Observer { + void itemAdded(Map map, K key, V value); + void itemRemoved(Map map, K key, V oldValue); + void itemReplaced(Map map, K key, V oldValue, V newValue); + } +} diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Player.java --- 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 { diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerList.java --- 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 list = new LinkedList(); - private List observers = new LinkedList(); +public class PlayerList extends ObservableLinkedHashMap { private long nextId = 1; - public List 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 unmodifiableList = Collections.unmodifiableList(list); - for(Observer o : observers) { - o.itemAdded(unmodifiableList, p); - } - } - - void removePlayer(String name) { - for(Iterator iter = list.iterator(); iter.hasNext();) { - Player p = iter.next(); - if(name.equals(p.name)) { - iter.remove(); - List unmodifiableList = Collections.unmodifiableList(list); - for(Observer o : observers) { - o.itemDeleted(unmodifiableList, p); - } - } - } - } - - public static interface Observer { - void itemAdded(List newList, Player added); - void itemDeleted(List newList, Player deleted); - } + public interface Observer extends ObservableLinkedHashMap.Observer {} } diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerListAdapter.java --- 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 newList, Player added) { - setPlayerList(newList); + public void itemAdded(Map map, String key, Player value) { + setPlayerList(map.values()); } - - public void itemDeleted(List newList, Player deleted) { - setPlayerList(newList); + + public void itemRemoved(Map map, String key, Player oldValue) { + setPlayerList(map.values()); + } + + public void itemReplaced(Map map, String key, + Player oldValue, Player newValue) { + setPlayerList(map.values()); } public void setPlayerList(Collection players) { diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/PlayerlistFragment.java --- 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; } }; diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Room.java --- 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; + } +} diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomList.java --- /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 { + 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 { } +} diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java --- /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 rooms = new ArrayList(); + 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 rooms) { + this.rooms = new ArrayList(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 map, String key, Room value) { + setList(map.values()); + } + + public void itemRemoved(Map map, String key, Room oldValue) { + setList(map.values()); + } + + public void itemReplaced(Map map, String key, Room oldValue, + Room newValue) { + setList(map.values()); + } +} \ No newline at end of file diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomlistFragment.java --- 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 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(); - 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; + } + }; } diff -r 62043f5f7c67 -r 0e29eec2df5c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TestActivity.java --- 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(); } } - }*/ + } }