summaryrefslogtreecommitdiff
path: root/same-android/src/main/java/com/orbekk/same/android
diff options
context:
space:
mode:
authorKjetil Ørbekk <kjetil.orbekk@gmail.com>2012-03-02 16:04:55 +0100
committerKjetil Ørbekk <kjetil.orbekk@gmail.com>2012-03-02 16:04:55 +0100
commit4474ee26b3eee38d1ad33bb3d771d6f804fedfcb (patch)
treea39c809a58b3e9b2a5f2e81942a8cd6e56d647ae /same-android/src/main/java/com/orbekk/same/android
parentf48ad7f90ad1d494299b08f5e1866ccb63ee7b2d (diff)
Move all Android cleasses to com.orbekk.same.android package.
Diffstat (limited to 'same-android/src/main/java/com/orbekk/same/android')
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/ClientInterfaceBridge.java1
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/GameController.java72
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/GameView.java185
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/GraphicsActivity.java38
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/MainActivity.java61
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/SameControllerActivity.java177
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/SameService.java265
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/StateViewerActivity.java59
-rw-r--r--same-android/src/main/java/com/orbekk/same/android/VariableTestActivity.java85
9 files changed, 942 insertions, 1 deletions
diff --git a/same-android/src/main/java/com/orbekk/same/android/ClientInterfaceBridge.java b/same-android/src/main/java/com/orbekk/same/android/ClientInterfaceBridge.java
index 72dfdb7..081b7ed 100644
--- a/same-android/src/main/java/com/orbekk/same/android/ClientInterfaceBridge.java
+++ b/same-android/src/main/java/com/orbekk/same/android/ClientInterfaceBridge.java
@@ -18,7 +18,6 @@ import android.os.Messenger;
import android.os.RemoteException;
import com.orbekk.same.ClientInterface;
-import com.orbekk.same.SameService;
import com.orbekk.same.State;
import com.orbekk.same.State.Component;
import com.orbekk.same.StateChangedListener;
diff --git a/same-android/src/main/java/com/orbekk/same/android/GameController.java b/same-android/src/main/java/com/orbekk/same/android/GameController.java
new file mode 100644
index 0000000..2678208
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/GameController.java
@@ -0,0 +1,72 @@
+package com.orbekk.same.android;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.orbekk.same.StateChangedListener;
+import com.orbekk.same.UpdateConflict;
+
+import android.graphics.Paint;
+
+public class GameController {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private List<Player> remotePlayers = new ArrayList<Player>();
+ private Player localPlayer;
+ private ChangeListener changeListener = null;
+// private SameInterface same;
+
+ public static class Player {
+ public Paint color;
+ public float posX;
+ public float posY;
+ }
+
+ public interface ChangeListener {
+ void playerStatesChanged();
+ }
+
+ public static Player newPlayer() {
+ Player player = new Player();
+ player.color = new Paint();
+ player.color.setARGB(255, 255, 0, 0);
+ player.posX = 0.5f;
+ player.posY = 0.5f;
+ return player;
+ }
+
+ public static GameController create(Player localPlayer) {
+ GameController controller = new GameController(localPlayer);
+// same.addStateChangedListener(controller);
+ return controller;
+ }
+
+ GameController(Player localPlayer) {
+ this.localPlayer = localPlayer;
+ }
+
+ public void setMyPosition(float x, float y) throws UpdateConflict {
+ this.localPlayer.posX = x;
+ this.localPlayer.posY = y;
+ changeListener.playerStatesChanged();
+ }
+
+ public Player getLocalPlayer() {
+ return localPlayer;
+ }
+
+ public List<Player> getRemotePlayers() {
+ return remotePlayers;
+ }
+
+ public void setChangeListener(ChangeListener listener) {
+ this.changeListener = listener;
+ }
+
+// @Override
+// public void stateChanged(String id, String data) {
+// logger.info("StateChanged({}, {})", id, data);
+// }
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/GameView.java b/same-android/src/main/java/com/orbekk/same/android/GameView.java
new file mode 100644
index 0000000..c3485bb
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/GameView.java
@@ -0,0 +1,185 @@
+package com.orbekk.same.android;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.orbekk.same.Variable;
+import com.orbekk.same.VariableUpdaterTask;
+import com.orbekk.same.Variable.OnChangeListener;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+public class GameView extends SurfaceView implements SurfaceHolder.Callback {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private GameThread thread;
+
+ static class Player {
+ public Player() {
+ }
+ public Player(float posX, float posY) {
+ this.posX = posX;
+ this.posY = posY;
+ }
+
+ public float posX;
+ public float posY;
+ }
+
+ static class GameThread extends Thread
+ implements Variable.OnChangeListener<Player> {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private int height = 0;
+ private int width = 0;
+ private SurfaceHolder holder;
+ private Context context;
+ private Paint background;
+ private Variable<Player> player;
+ private VariableUpdaterTask<Player> updater;
+ private AtomicBoolean shouldRedraw = new AtomicBoolean(true);
+
+ private Paint color = new Paint();
+
+ public GameThread(SurfaceHolder holder, Context context,
+ Variable<Player> player) {
+ this.holder = holder;
+ this.context = context;
+ this.player = player;
+ background = new Paint();
+ background.setARGB(255, 0, 0, 0);
+ color.setARGB(255, 255, 0, 0);
+ }
+
+ public void setUp() {
+ player.addOnChangeListener(this);
+ updater = new VariableUpdaterTask<Player>(player);
+ updater.set(new Player(0.5f, 0.5f));
+ updater.start();
+ }
+
+ public void tearDown() {
+ player.removeOnChangeListener(this);
+ updater.interrupt();
+ }
+
+ public void setSize(int width, int height) {
+ synchronized(holder) {
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ private void doDraw(Canvas c) {
+ c.drawRect(0.0f, 0.0f, width+1.0f, height+1.0f, background);
+ Player player_ = player.get();
+ if (player_ == null) {
+ return;
+ }
+ c.drawCircle(player_.posX * width, player_.posY * height,
+ 20.0f, color);
+ }
+
+ @Override public void run() {
+ while (true) {
+ Canvas c = null;
+ try {
+ c = holder.lockCanvas();
+ if (c != null) {
+ synchronized(holder) {
+ doDraw(c);
+ }
+ }
+ } finally {
+ if (c != null) {
+ holder.unlockCanvasAndPost(c);
+ }
+ }
+ synchronized (this) {
+ if (Thread.interrupted()) {
+ break;
+ }
+ try {
+ while (!shouldRedraw.get()) {
+ wait();
+ }
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ }
+
+ private synchronized void setShouldRedraw() {
+ shouldRedraw.set(true);
+ notifyAll();
+ }
+
+ private synchronized void setPosition(final float x, final float y) {
+ if (player.get() == null || player.get().posX != x ||
+ player.get().posY != y) {
+ Player newPlayer = new Player(x / width, y / height);
+ updater.set(newPlayer);
+ }
+ }
+
+ @Override
+ public synchronized void valueChanged(Variable<Player> unused) {
+ logger.info("Variable updated.");
+ player.update();
+ setShouldRedraw();
+ }
+ }
+
+ public GameView(Context context, Variable<Player> player) {
+ super(context);
+ getHolder().addCallback(this);
+ thread = new GameThread(getHolder(), context, player);
+ }
+
+ public void setUp() {
+ thread.setUp();
+ }
+
+ public void tearDown() {
+ thread.tearDown();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ Paint paint = new Paint();
+ paint.setARGB(255, 255, 0, 0);
+ canvas.drawCircle(50.0f, 50.0f, 50.0f, paint);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ logger.info("SurfaceChanged(w={}, h={})", width, height);
+ thread.setSize(width, height);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ logger.info("SurfaceCreated()");
+ thread.start();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ logger.info("SurfaceDestroyed()");
+ thread.interrupt();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ thread.setPosition(e.getX(), e.getY());
+ return true;
+ }
+
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/GraphicsActivity.java b/same-android/src/main/java/com/orbekk/same/android/GraphicsActivity.java
new file mode 100644
index 0000000..dab26a7
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/GraphicsActivity.java
@@ -0,0 +1,38 @@
+package com.orbekk.same.android;
+
+import org.codehaus.jackson.type.TypeReference;
+
+import com.orbekk.same.Variable;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class GraphicsActivity extends Activity {
+ private GameView gameView;
+ private ClientInterfaceBridge client;
+
+ @Override
+ public void onCreate(Bundle savedBundle) {
+ super.onCreate(savedBundle);
+ }
+
+ public void onResume() {
+ super.onResume();
+ client = new ClientInterfaceBridge(this);
+ client.connect();
+ TypeReference playerType = new TypeReference<GameView.Player>() {};
+ Variable<GameView.Player> player = client.createVariableFactory()
+ .create("Player", playerType);
+ gameView = new GameView(this, player);
+ gameView.setUp();
+ setContentView(gameView);
+ }
+
+ public void onStop() {
+ super.onStop();
+ gameView.tearDown();
+ gameView = null;
+ client.disconnect();
+ client = null;
+ }
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/MainActivity.java b/same-android/src/main/java/com/orbekk/same/android/MainActivity.java
new file mode 100644
index 0000000..87ec967
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/MainActivity.java
@@ -0,0 +1,61 @@
+package com.orbekk.same.android;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.orbekk.same.android.R;
+import com.orbekk.same.android.benchmark.RepeatedSetVariableActivity;
+
+public class MainActivity extends Activity {
+ Logger logger = LoggerFactory.getLogger(getClass());
+
+ public final static Map<String, Class<? extends Activity>> activities;
+ static {
+ activities = new HashMap<String, Class<? extends Activity>>();
+ activities.put("Same settings", SameControllerActivity.class);
+ activities.put("Variable test", VariableTestActivity.class);
+ activities.put("State monitor", StateViewerActivity.class);
+ activities.put("Graphics demo", GraphicsActivity.class);
+ activities.put("Benchmark", RepeatedSetVariableActivity.class);
+ }
+
+ public final static List<String> activityList =
+ new ArrayList<String>(activities.keySet());
+
+ private AdapterView.OnItemClickListener activityListClickListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> unused_parent, View unused_view,
+ int position, long id) {
+ String activityName = activityList.get(position);
+ Class<? extends Activity> activity = activities.get(activityName);
+ startActivity(new Intent(MainActivity.this, activity));
+ }
+ };
+
+ private void createActivityList() {
+ ListView list = (ListView)findViewById(R.id.activities_menu);
+ list.setAdapter(new ArrayAdapter<String>(
+ this, R.layout.list_text_item, activityList));
+ list.setOnItemClickListener(activityListClickListener);
+ }
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ createActivityList();
+ }
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/SameControllerActivity.java b/same-android/src/main/java/com/orbekk/same/android/SameControllerActivity.java
new file mode 100644
index 0000000..7700e2f
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/SameControllerActivity.java
@@ -0,0 +1,177 @@
+package com.orbekk.same.android;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.orbekk.same.android.R;
+import com.orbekk.same.android.net.Broadcaster;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class SameControllerActivity extends Activity {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private Messenger sameService = null;
+ private List<String> networkNames = new ArrayList<String>();
+ private List<String> networkUrls = new ArrayList<String>();
+
+ private ServiceConnection sameConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ sameService = new Messenger(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sameService = null;
+ }
+ };
+
+ private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public synchronized void onReceive(Context context, Intent intent) {
+ if (SameService.AVAILABLE_NETWORKS_UPDATE.equals(intent.getAction())) {
+ networkNames = intent.getStringArrayListExtra(
+ SameService.AVAILABLE_NETWORKS);
+ networkUrls = intent.getStringArrayListExtra(
+ SameService.NETWORK_URLS);
+ updateNetworkList();
+ }
+ }
+ };
+
+ private AdapterView.OnItemClickListener networkListClickListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String networkName = networkNames.get(position);
+ int networkIndex = networkNames.indexOf(networkName);
+ String masterUrl = networkUrls.get(networkIndex);
+ joinNetwork(masterUrl);
+ }
+ };
+
+ private void updateNetworkList() {
+ ListView list = (ListView)findViewById(R.id.network_list);
+ list.setAdapter(new ArrayAdapter<String>(
+ SameControllerActivity.this,
+ R.layout.list_text_item, networkNames));
+ }
+
+
+ public void createNetwork(View unused) {
+ Message message = Message.obtain(null, SameService.CREATE_NETWORK);
+ try {
+ sameService.send(message);
+ } catch (RemoteException e) {
+ logger.error("Failed to create network", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void joinNetworkUrl(View unused) {
+ String masterUrl = "";
+ Intent intent = new Intent(this, SameService.class);
+ intent.setAction("join");
+ EditText t = (EditText)findViewById(R.id.master_service_url);
+ masterUrl = t.getText().toString();
+ if (!masterUrl.startsWith("http://")) {
+ masterUrl = "http://" + masterUrl;
+ }
+ if (!masterUrl.endsWith("/MasterService.json")) {
+ masterUrl += "/MasterService.json";
+ }
+ joinNetwork(masterUrl);
+ }
+
+ private void joinNetwork(String masterUrl) {
+ logger.info("joinNetwork({})", masterUrl);
+ Message message = Message.obtain(null, SameService.JOIN_NETWORK);
+ message.getData().putString("masterUrl", masterUrl);
+ try {
+ sameService.send(message);
+ } catch (RemoteException e) {
+ logger.error("Failed to send message", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void showIpAddress() {
+ TextView t = (TextView)findViewById(R.id.ipAddress);
+ t.setText("My IP: ");
+ t.append(new Broadcaster(this).getWlanAddress().getHostAddress());
+ }
+
+ public void doneClicked(View unused) {
+ finish();
+ }
+
+ public void searchNetworks(View unused) {
+ logger.info("SearchNetworks()");
+ Message searchMessage = Message.obtain(null, SameService.SEARCH_NETWORKS);
+ try {
+ sameService.send(searchMessage);
+ } catch (RemoteException e) {
+ logger.error("Failed to send message", e);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ System.setProperty("java.net.preferIPv4Stack", "true");
+ System.setProperty("java.net.preferIPv6Addresses", "false");
+
+ setContentView(R.layout.controller);
+ showIpAddress();
+
+ ListView networkList = (ListView)findViewById(R.id.network_list);
+ networkList.setOnItemClickListener(networkListClickListener);
+ }
+
+ @Override public void onResume() {
+ super.onResume();
+
+ Intent intent = new Intent(this, SameService.class);
+ bindService(intent, sameConnection, Context.BIND_AUTO_CREATE);
+
+ IntentFilter sameServiceUpdates = new IntentFilter(
+ SameService.AVAILABLE_NETWORKS_UPDATE);
+ registerReceiver(broadcastReceiver, sameServiceUpdates);
+ }
+
+ @Override public void onStop() {
+ super.onStop();
+ if (sameService != null) {
+ unbindService(sameConnection);
+ }
+ unregisterReceiver(broadcastReceiver);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+}
+
diff --git a/same-android/src/main/java/com/orbekk/same/android/SameService.java b/same-android/src/main/java/com/orbekk/same/android/SameService.java
new file mode 100644
index 0000000..f5116ba
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/SameService.java
@@ -0,0 +1,265 @@
+package com.orbekk.same.android;
+
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.widget.Toast;
+
+import com.orbekk.same.NetworkNotificationListener;
+import com.orbekk.same.SameController;
+import com.orbekk.same.State;
+import com.orbekk.same.StateChangedListener;
+import com.orbekk.same.State.Component;
+import com.orbekk.same.android.net.AndroidBroadcasterFactory;
+import com.orbekk.same.android.net.Broadcaster;
+import com.orbekk.same.config.Configuration;
+import com.orbekk.util.DelayedOperation;
+
+public class SameService extends Service {
+ public final static int SEARCH_NETWORKS = 2;
+ public final static int CREATE_NETWORK = 3;
+
+ /**
+ * masterUrl: getData().getString("masterUrl")
+ */
+ public final static int JOIN_NETWORK = 4;
+ public final static int ADD_STATE_RECEIVER = 5;
+ public final static int REMOVE_STATE_RECEIVER = 6;
+
+ /**
+ * arg1: Operation number.
+ * bundle: A Bundle created with ComponentBundle
+ */
+ public final static int SET_STATE = 7;
+
+ /**
+ * bundle: A Bundle created with ComponentBundle.
+ */
+ public final static int UPDATED_STATE_CALLBACK = 8;
+
+ /**
+ * arg1: Operation number.
+ * arg2: Status code.
+ * obj: Status message.
+ */
+ public final static int OPERATION_STATUS_CALLBACK = 9;
+
+ // TODO: Remove these and use messengers instead of broadcast intents.
+ public final static String AVAILABLE_NETWORKS_UPDATE =
+ "com.orbekk.same.SameService.action.AVAILABLE_NETWORKS_UPDATE";
+ public final static String AVAILABLE_NETWORKS =
+ "com.orbekk.same.SameService.action.AVAILABLE_NETWORKS";
+ public final static String NETWORK_URLS =
+ "com.orbekk.same.SameService.action.NETWORK_URLS";
+
+ final static int SERVICE_PORT = 15068;
+ final static int DISCOVERY_PORT = 15066;
+
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private SameController sameController = null;
+ private Configuration configuration = null;
+ private Vector<Messenger> stateReceivers = new Vector<Messenger>();
+
+ private ArrayList<String> networkNames = new ArrayList<String>();
+ private ArrayList<String> networkUrls = new ArrayList<String>();
+
+ private NetworkNotificationListener networkListener =
+ new NetworkNotificationListener() {
+ @Override
+ public void notifyNetwork(String networkName, String masterUrl) {
+ logger.info("notifyNetwork({})", networkName);
+ networkNames.add(networkName);
+ networkUrls.add(masterUrl);
+ Intent intent = new Intent(AVAILABLE_NETWORKS_UPDATE);
+ intent.putStringArrayListExtra(AVAILABLE_NETWORKS,
+ networkNames);
+ intent.putStringArrayListExtra(NETWORK_URLS,
+ networkUrls);
+ sendBroadcast(intent);
+ }
+ };
+
+ class InterfaceHandler extends Handler {
+ @Override public void handleMessage(Message message) {
+ switch (message.what) {
+ case SEARCH_NETWORKS:
+ logger.info("SEARCH_NETWORKS");
+ sameController.searchNetworks();
+ break;
+ case CREATE_NETWORK:
+ logger.info("CREATE_NETWORK");
+ create();
+ break;
+ case JOIN_NETWORK:
+ logger.info("JOIN_NETWORK");
+ String masterUrl = message.getData().getString("masterUrl");
+ sameController.getClient().joinNetwork(masterUrl);
+ break;
+ case ADD_STATE_RECEIVER:
+ logger.info("ADD_STATE_RECEIVER: {}", message);
+ Messenger messenger = message.replyTo;
+ if (messenger != null) {
+ stateReceivers.add(messenger);
+ sendAllState(messenger);
+ } else {
+ logger.error("ADD_STATE_RECEIVER: Missing Messenger.");
+ }
+ break;
+ case REMOVE_STATE_RECEIVER:
+ logger.info("REMOVE_STATE_RECEIVER: {}", message);
+ Messenger droppedMessenger = (Messenger)message.obj;
+ stateReceivers.remove(droppedMessenger);
+ break;
+ case SET_STATE:
+ logger.info("SET_STATE: oId: {}, comp: {}", message.arg1, message.obj);
+ State.Component updatedComponent =
+ new ComponentBundle(message.getData()).getComponent();
+ int id = message.arg1;
+ logger.info("Running operation. Component: " + updatedComponent);
+ DelayedOperation op = sameController.getClient().getInterface()
+ .set(updatedComponent);
+ logger.info("Operation finished. Sending callback.");
+ operationStatusCallback(op, id, message.replyTo);
+ logger.info("Callback sent.");
+ break;
+ default:
+ super.handleMessage(message);
+ }
+ logger.info("Finished handling message.");
+ }
+ }
+
+ private final Messenger messenger = new Messenger(new InterfaceHandler());
+
+ private StateChangedListener stateListener = new StateChangedListener() {
+ @Override
+ public void stateChanged(Component component) {
+ synchronized (stateReceivers) {
+ ArrayList<Messenger> dropped = new ArrayList<Messenger>();
+ for (Messenger messenger : stateReceivers) {
+ Message message = Message.obtain(null, UPDATED_STATE_CALLBACK);
+ message.setData(new ComponentBundle(component).getBundle());
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ logger.warn("Failed to send update. Dropping state receiver.");
+ e.printStackTrace();
+ dropped.add(messenger);
+ }
+ }
+ stateReceivers.removeAll(dropped);
+ }
+ }
+ };
+
+ private void operationStatusCallback(DelayedOperation op, int id, Messenger replyTo) {
+ op.waitFor();
+ synchronized (stateReceivers) {
+ Message message = Message.obtain(null,
+ OPERATION_STATUS_CALLBACK);
+ message.arg1 = id;
+ message.arg2 = op.getStatus().getStatusCode();
+ message.obj = op.getStatus().getMessage();
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ logger.warn("Unable to send update result: " +
+ op.getStatus());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void sendAllState(Messenger messenger) {
+ State state = sameController.getClient().getInterface().getState();
+ for (Component c : state.getComponents()) {
+ Message message = Message.obtain(null, UPDATED_STATE_CALLBACK);
+ message.setData(new ComponentBundle(c).getBundle());
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ logger.warn("Failed to send state.");
+ e.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ private void initializeConfiguration() {
+ Properties properties = new Properties();
+ String localIp = new Broadcaster(this)
+ .getWlanAddress().getHostAddress();
+ String localMaster = "http://" + localIp + ":" + SERVICE_PORT +
+ "/MasterService.json";
+ properties.setProperty("port", ""+SERVICE_PORT);
+ properties.setProperty("localIp", localIp);
+ properties.setProperty("masterUrl", localMaster);
+ properties.setProperty("enableDiscovery", "true");
+ properties.setProperty("discoveryPort", ""+DISCOVERY_PORT);
+ properties.setProperty("networkName", "AndroidNetwork");
+ configuration = new Configuration(properties);
+ }
+
+ /** Create a public network. */
+ private void create() {
+ sameController.getClient().joinNetwork(
+ configuration.get("masterUrl"));
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ logger.info("onBind()");
+
+ // Make sure service continues to run after it is unbound.
+ Intent service = new Intent(this, getClass());
+ startService(service);
+
+ return messenger.getBinder();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ logger.info("onStartCommand()");
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ logger.info("onCreate()");
+
+ if (sameController == null) {
+ initializeConfiguration();
+ sameController = SameController.create(
+ new AndroidBroadcasterFactory(this),
+ configuration);
+ try {
+ sameController.start();
+ sameController.getClient().setNetworkListener(networkListener);
+ sameController.getClient().getInterface()
+ .addStateListener(stateListener);
+ } catch (Exception e) {
+ logger.error("Failed to start server", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ logger.info("onDestroy()");
+ if (sameController != null) {
+ sameController.stop();
+ }
+ }
+
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/StateViewerActivity.java b/same-android/src/main/java/com/orbekk/same/android/StateViewerActivity.java
new file mode 100644
index 0000000..679e3b6
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/StateViewerActivity.java
@@ -0,0 +1,59 @@
+package com.orbekk.same.android;
+
+import java.util.ArrayList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.orbekk.same.android.R;
+import com.orbekk.same.State;
+import com.orbekk.same.StateChangedListener;
+import com.orbekk.same.State.Component;
+
+public class StateViewerActivity extends Activity {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private ClientInterfaceBridge client;
+
+ private StateChangedListener stateListener = new StateChangedListener() {
+ @Override
+ public void stateChanged(Component component) {
+ displayState();
+ }
+ };
+
+ private void displayState() {
+ ArrayList<String> contentList = new ArrayList<String>();
+ for (State.Component component : client.getState().getComponents()) {
+ contentList.add(component.toString());
+ }
+ ListView list = (ListView)findViewById(R.id.state_view_list);
+ list.setAdapter(new ArrayAdapter<String>(
+ this, R.layout.list_text_item, contentList));
+ }
+
+ @Override public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ setContentView(R.layout.state_viewer);
+ }
+
+ @Override public void onResume() {
+ super.onResume();
+ client = new ClientInterfaceBridge(this);
+ client.addStateListener(stateListener);
+ client.connect();
+ }
+
+ @Override public void onStop() {
+ super.onStop();
+ client.removeStateListener(stateListener);
+ client.disconnect();
+ client = null;
+ }
+
+}
diff --git a/same-android/src/main/java/com/orbekk/same/android/VariableTestActivity.java b/same-android/src/main/java/com/orbekk/same/android/VariableTestActivity.java
new file mode 100644
index 0000000..7560a41
--- /dev/null
+++ b/same-android/src/main/java/com/orbekk/same/android/VariableTestActivity.java
@@ -0,0 +1,85 @@
+package com.orbekk.same.android;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.orbekk.same.android.R;
+import com.orbekk.same.Variable;
+import com.orbekk.same.android.R.id;
+import com.orbekk.same.android.R.layout;
+import com.orbekk.same.Variable.OnChangeListener;
+import com.orbekk.util.DelayedOperation;
+
+public class VariableTestActivity extends Activity {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private ClientInterfaceBridge client;
+ private Variable<String> variable;
+
+ private Variable.OnChangeListener<String> onChangeListener =
+ new Variable.OnChangeListener<String>() {
+ @Override
+ public void valueChanged(Variable<String> unused) {
+ variable.update();
+ displayVariable();
+ }
+ };
+
+ private class UpdateVariableTask
+ extends AsyncTask<String, Void, DelayedOperation.Status> {
+ @Override protected DelayedOperation.Status doInBackground(String... values) {
+ String value = values[0];
+ return variable.set(value).getStatus();
+ }
+
+ @Override protected void onPostExecute(DelayedOperation.Status status) {
+ if (!status.isOk()) {
+ Toast.makeText(VariableTestActivity.this,
+ "Update failed: " + status, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ }
+
+ private void displayVariable() {
+ TextView tv = (TextView)findViewById(R.id.variable_text);
+ if (variable.get() != null) {
+ tv.setText(variable.get());
+ }
+ }
+
+ public void setVariable(View unused) {
+ EditText et = (EditText)findViewById(R.id.set_variable_text);
+ String newValue = et.getText().toString();
+ new UpdateVariableTask().execute(newValue);
+ }
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.variable_test);
+ }
+
+ @Override public void onResume() {
+ super.onResume();
+ client = new ClientInterfaceBridge(this);
+ client.connect();
+ variable = client.createVariableFactory()
+ .createString("TestVariable");
+ variable.addOnChangeListener(onChangeListener);
+ variable.set("Hello, World!");
+ displayVariable();
+ }
+
+ @Override public void onStop() {
+ super.onStop();
+ variable.removeOnChangeListener(onChangeListener);
+ client.disconnect();
+ }
+}