Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Morlunk/Jumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Comminos <andrew@comminos.com>2015-10-23 07:09:20 +0300
committerAndrew Comminos <andrew@comminos.com>2015-10-23 07:09:20 +0300
commit551e79877c292ee786f4aa76eb3b9818ec0892d7 (patch)
tree2f5f00c212b278cf22ac3a87ae6cc4185a3b0686
parent523e9357752a68024e1525a8354a804d76eedf5f (diff)
Make JumbleBinder a local proxy, migrate most functionality to JumbleService to reduce coupling.
-rw-r--r--src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java10
-rw-r--r--src/main/java/com/morlunk/jumble/IJumbleService.java225
-rw-r--r--src/main/java/com/morlunk/jumble/JumbleBinder.java412
-rw-r--r--src/main/java/com/morlunk/jumble/JumbleService.java334
4 files changed, 552 insertions, 429 deletions
diff --git a/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java b/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java
index d309f55..0776335 100644
--- a/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java
+++ b/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java
@@ -21,7 +21,6 @@ import android.content.Intent;
import android.os.RemoteException;
import android.test.ServiceTestCase;
-import com.morlunk.jumble.JumbleBinder;
import com.morlunk.jumble.JumbleService;
import com.morlunk.jumble.model.Server;
@@ -45,11 +44,10 @@ public class JumbleServiceTest extends ServiceTestCase<JumbleService> {
intent.putExtra(JumbleService.EXTRAS_SERVER, DUMMY_SERVER);
startService(intent);
JumbleService service = getService();
- JumbleBinder binder = (JumbleBinder) service.onBind(intent);
- assertFalse(binder.isReconnecting());
- assertNull(binder.getConnectionError());
- assertEquals(JumbleService.ConnectionState.DISCONNECTED, binder.getConnectionState());
- assertEquals(DUMMY_SERVER, binder.getConnectedServer());
+ assertFalse(service.isReconnecting());
+ assertNull(service.getConnectionError());
+ assertEquals(JumbleService.ConnectionState.DISCONNECTED, service.getConnectionState());
+ assertEquals(DUMMY_SERVER, service.getConnectedServer());
}
}
diff --git a/src/main/java/com/morlunk/jumble/IJumbleService.java b/src/main/java/com/morlunk/jumble/IJumbleService.java
new file mode 100644
index 0000000..6e149d0
--- /dev/null
+++ b/src/main/java/com/morlunk/jumble/IJumbleService.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 Andrew Comminos <andrew@comminos.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.morlunk.jumble;
+
+import com.morlunk.jumble.model.IChannel;
+import com.morlunk.jumble.model.IUser;
+import com.morlunk.jumble.model.Message;
+import com.morlunk.jumble.model.Server;
+import com.morlunk.jumble.net.JumbleUDPMessageType;
+import com.morlunk.jumble.util.IJumbleObserver;
+import com.morlunk.jumble.util.JumbleException;
+
+import java.util.List;
+
+/**
+ * A public interface for clients to communicate with a {@link JumbleService}.
+ * The long-term goal for this class is to migrate of the complexity out of this class into a
+ * JumbleProtocol class that is owned by a {@link com.morlunk.jumble.net.JumbleConnection}.
+ * <br><br>
+ * Calls are not guaranteed to be thread-safe, so only call the binder from the main thread.
+ * Service state changes related to connection state are only guaranteed to work if isConnected()
+ * is checked to be true.
+ * <br><br>
+ * If not explicitly stated in the method documentation, any call that depends on connection state
+ * will throw IllegalStateException if disconnected or not synchronized.
+ */
+public interface IJumbleService {
+ /**
+ * Returns the current connection state of the service.
+ * @return one of {@link JumbleService.ConnectionState}.
+ */
+ JumbleService.ConnectionState getConnectionState();
+
+ /**
+ * If the {@link JumbleService} disconnected due to an error, returns that error.
+ * @return The error causing disconnection. If the last disconnection was successful or a
+ * connection has yet to be established, returns null.
+ */
+ JumbleException getConnectionError();
+
+ /**
+ * Returns the reconnection state of the {@link JumbleService}.
+ * @return true if the service will attempt to automatically reconnect in the future.
+ */
+ boolean isReconnecting();
+
+ /**
+ * Cancels any future reconnection attempts. Does nothing if reconnection is not in progress.
+ */
+ void cancelReconnect();
+
+ /**
+ * @return the latency in milliseconds for the TCP connection.
+ * @throws IllegalStateException if not connected.
+ */
+ long getTCPLatency();
+
+ /**
+ * @return the latency in milliseconds for the UDP connection.
+ * @throws IllegalStateException if not connected.
+ */
+ long getUDPLatency();
+
+ /**
+ * @return the maximum bandwidth in bps for audio allowed by the server, or -1 if not set.
+ * @throws IllegalStateException if not synchronized.
+ */
+ int getMaxBandwidth();
+
+ /**
+ * @return the current bandwidth in bps for audio sent to the server, or a negative integer
+ * if unknown (prior to connection or after disconnection).
+ * @throws IllegalStateException if not synchronized.
+ */
+ int getCurrentBandwidth();
+
+ /**
+ * Returns the protocol version returned by the server in the format 0xAABBCC, where AA
+ * indicates the major version, BB indicates the minor version, and CC indicates the patch
+ * version. This is the same formatting used by the Mumble protocol in big-endian format.
+ * @return the current bandwidth in bps for audio sent to the server, or a negative integer
+ * if unknown (prior to connection or after disconnection).
+ * @throws IllegalStateException if not synchronized.
+ */
+ int getServerVersion();
+
+ /**
+ * @return a user-readable string with the server's Mumble release info.
+ * @throws IllegalStateException if not synchronized.
+ */
+ String getServerRelease();
+
+ /**
+ * @return a user-readable string with the server's OS name.
+ * @throws IllegalStateException if not connected.
+ */
+ String getServerOSName();
+
+ /**
+ * @return a user-readable string with the server's OS version.
+ * @throws IllegalStateException if not synchronized.
+ */
+ String getServerOSVersion();
+
+ /**
+ * Returns the current user's session. Set during server synchronization.
+ * @return an integer identifying the current user's connection.
+ * @throws IllegalStateException if not synchronized.
+ */
+ int getSession();
+
+ /**
+ * Returns the current user. Set during server synchronization.
+ * @return the {@link IUser} representing the current user.
+ * @throws IllegalStateException if not synchronized.
+ */
+ IUser getSessionUser();
+
+ /**
+ * Returns the user's current channel.
+ * @return the {@link IChannel} representing the user's current channel.
+ * @throws IllegalStateException if not synchronized.
+ */
+ IChannel getSessionChannel();
+
+ /**
+ * @return the server that Jumble is currently connected to (or attempted connection to).
+ */
+ Server getConnectedServer();
+
+ /**
+ * Retrieves the user with the given session ID.
+ * @param session An integer ID identifying a user's session. See {@link IUser#getSession()}.
+ * @return A user with the given session, or null if not found.
+ * @throws IllegalStateException if not synchronized.
+ */
+ IUser getUser(int session);
+
+ /**
+ * Retrieves the channel with the given ID.
+ * @param id An integer ID identifying a channel. See {@link IChannel#getId()}.
+ * @return A channel with the given session, or null if not found.
+ * @throws IllegalStateException if not synchronized.
+ */
+ IChannel getChannel(int id);
+
+ /**
+ * @return the root channel of the server.
+ * @throws IllegalStateException if not synchronized.
+ */
+ IChannel getRootChannel();
+
+ int getPermissions();
+
+ int getTransmitMode();
+
+ JumbleUDPMessageType getCodec();
+
+ boolean usingBluetoothSco();
+
+ void enableBluetoothSco();
+
+ void disableBluetoothSco();
+
+ boolean isTalking();
+
+ void setTalkingState(boolean talking);
+
+ void joinChannel(int channel);
+
+ void moveUserToChannel(int session, int channel);
+
+ void createChannel(int parent, String name, String description, int position, boolean temporary);
+
+ void sendAccessTokens(List<String> tokens);
+
+ void requestBanList();
+
+ void requestUserList();
+
+ void requestPermissions(int channel);
+
+ void requestComment(int session);
+
+ void requestAvatar(int session);
+
+ void requestChannelDescription(int channel);
+
+ void registerUser(int session);
+
+ void kickBanUser(int session, String reason, boolean ban);
+
+ Message sendUserTextMessage(int session, String message);
+
+ Message sendChannelTextMessage(int channel, String message, boolean tree);
+
+ void setUserComment(int session, String comment);
+
+ void setPrioritySpeaker(int session, boolean priority);
+
+ void removeChannel(int channel);
+
+ void setMuteDeafState(int session, boolean mute, boolean deaf);
+
+ void setSelfMuteDeafState(boolean mute, boolean deaf);
+
+ void registerObserver(IJumbleObserver observer);
+
+ void unregisterObserver(IJumbleObserver observer);
+}
diff --git a/src/main/java/com/morlunk/jumble/JumbleBinder.java b/src/main/java/com/morlunk/jumble/JumbleBinder.java
deleted file mode 100644
index b0db425..0000000
--- a/src/main/java/com/morlunk/jumble/JumbleBinder.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2015 Andrew Comminos <andrew@comminos.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.morlunk.jumble;
-
-import android.os.Binder;
-import android.util.Log;
-
-import com.morlunk.jumble.exception.AudioException;
-import com.morlunk.jumble.model.Channel;
-import com.morlunk.jumble.model.IChannel;
-import com.morlunk.jumble.model.IUser;
-import com.morlunk.jumble.model.Message;
-import com.morlunk.jumble.model.Server;
-import com.morlunk.jumble.model.User;
-import com.morlunk.jumble.net.JumbleConnection;
-import com.morlunk.jumble.net.JumbleTCPMessageType;
-import com.morlunk.jumble.net.JumbleUDPMessageType;
-import com.morlunk.jumble.protobuf.Mumble;
-import com.morlunk.jumble.util.IJumbleObserver;
-import com.morlunk.jumble.util.JumbleException;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A public interface for clients to communicate with a {@link JumbleService}.
- * The long-term goal for this class is to migrate of the complexity out of this class into a
- * JumbleProtocol class that is owned by a {@link com.morlunk.jumble.net.JumbleConnection}.
- * <br><br>
- * Calls are not guaranteed to be thread-safe, so only call the binder from the main thread.
- * Service state changes related to connection state are only guaranteed to work if isConnected()
- * is checked to be true.
- * <br><br>
- * If not explicitly stated in the method documentation, any call that depends on connection state
- * will throw IllegalStateException if disconnected or not synchronized.
- */
-public class JumbleBinder extends Binder {
- private final JumbleService mService;
-
- protected JumbleBinder(JumbleService service) {
- mService = service;
- }
-
- /**
- * Returns the current connection state of the service.
- * @return one of {@link com.morlunk.jumble.JumbleService.ConnectionState}.
- */
- public JumbleService.ConnectionState getConnectionState() {
- return mService.getConnectionState();
- }
-
- /**
- * If the {@link JumbleService} disconnected due to an error, returns that error.
- * @return The error causing disconnection. If the last disconnection was successful or a
- * connection has yet to be established, returns null.
- */
- public JumbleException getConnectionError() {
- JumbleConnection connection = mService.getConnection();
- return connection != null ? connection.getError() : null;
- }
-
- /**
- * Returns the reconnection state of the {@link JumbleService}.
- * @return true if the service will attempt to automatically reconnect in the future.
- */
- public boolean isReconnecting() {
- return mService.isReconnecting();
- }
-
- /**
- * Cancels any future reconnection attempts. Does nothing if reconnection is not in progress.
- */
- public void cancelReconnect() {
- mService.setReconnecting(false);
- }
-
- /**
- * @return the latency in milliseconds for the TCP connection.
- * @throws IllegalStateException if not connected.
- */
- public long getTCPLatency() {
- return mService.getConnection().getTCPLatency();
- }
-
- /**
- * @return the latency in milliseconds for the UDP connection.
- * @throws IllegalStateException if not connected.
- */
- public long getUDPLatency() {
- return mService.getConnection().getUDPLatency();
- }
-
- /**
- * @return the maximum bandwidth in bps for audio allowed by the server, or -1 if not set.
- * @throws IllegalStateException if not synchronized.
- */
- public int getMaxBandwidth() {
- return mService.getConnection().getMaxBandwidth();
- }
-
- /**
- * @return the current bandwidth in bps for audio sent to the server, or a negative integer
- * if unknown (prior to connection or after disconnection).
- * @throws IllegalStateException if not synchronized.
- */
- public int getCurrentBandwidth() {
- return mService.getAudioHandler().getCurrentBandwidth();
- }
-
- /**
- * Returns the protocol version returned by the server in the format 0xAABBCC, where AA
- * indicates the major version, BB indicates the minor version, and CC indicates the patch
- * version. This is the same formatting used by the Mumble protocol in big-endian format.
- * @return the current bandwidth in bps for audio sent to the server, or a negative integer
- * if unknown (prior to connection or after disconnection).
- * @throws IllegalStateException if not synchronized.
- */
- public int getServerVersion() {
- return mService.getConnection().getServerVersion();
- }
-
- /**
- * @return a user-readable string with the server's Mumble release info.
- * @throws IllegalStateException if not synchronized.
- */
- public String getServerRelease() {
- return mService.getConnection().getServerRelease();
- }
-
- /**
- * @return a user-readable string with the server's OS name.
- * @throws IllegalStateException if not connected.
- */
- public String getServerOSName() {
- return mService.getConnection().getServerOSName();
- }
-
- /**
- * @return a user-readable string with the server's OS version.
- * @throws IllegalStateException if not synchronized.
- */
- public String getServerOSVersion() {
- return mService.getConnection().getServerOSVersion();
- }
-
- /**
- * Returns the current user's session. Set during server synchronization.
- * @return an integer identifying the current user's connection.
- * @throws IllegalStateException if not synchronized.
- */
- public int getSession() {
- return mService.getConnection().getSession();
- }
-
- /**
- * Returns the current user. Set during server synchronization.
- * @return the {@link IUser} representing the current user.
- * @throws IllegalStateException if not synchronized.
- */
- public IUser getSessionUser() {
- return mService.getModelHandler().getUser(getSession());
- }
-
- /**
- * Returns the user's current channel.
- * @return the {@link IChannel} representing the user's current channel.
- * @throws IllegalStateException if not synchronized.
- */
- public IChannel getSessionChannel() {
- IUser user = getSessionUser();
- if (user != null)
- return user.getChannel();
- throw new IllegalStateException("Session user should be set post-synchronization!");
- }
-
- /**
- * @return the server that Jumble is currently connected to (or attempted connection to).
- */
- public Server getConnectedServer() {
- return mService.getConnectedServer();
- }
-
- /**
- * Retrieves the user with the given session ID.
- * @param session An integer ID identifying a user's session. See {@link IUser#getSession()}.
- * @return A user with the given session, or null if not found.
- * @throws IllegalStateException if not synchronized.
- */
- public IUser getUser(int session) {
- return mService.getModelHandler().getUser(session);
- }
-
- /**
- * Retrieves the channel with the given ID.
- * @param id An integer ID identifying a channel. See {@link IChannel#getId()}.
- * @return A channel with the given session, or null if not found.
- * @throws IllegalStateException if not synchronized.
- */
- public IChannel getChannel(int id) {
- return mService.getModelHandler().getChannel(id);
- }
-
- /**
- * @return the root channel of the server.
- * @throws IllegalStateException if not synchronized.
- */
- public IChannel getRootChannel() {
- return getChannel(0);
- }
-
- public int getPermissions() {
- return mService.getModelHandler().getPermissions();
- }
-
- public int getTransmitMode() {
- return mService.getAudioHandler().getTransmitMode();
- }
-
- public JumbleUDPMessageType getCodec() {
- return mService.getConnection().getCodec();
- }
-
- public boolean usingBluetoothSco() {
- return mService.getBluetoothReceiver().isBluetoothScoOn();
- }
-
- public void enableBluetoothSco() {
- mService.getBluetoothReceiver().startBluetoothSco();
- }
-
- public void disableBluetoothSco() {
- mService.getBluetoothReceiver().stopBluetoothSco();
- }
-
- public boolean isTalking() {
- return mService.getAudioHandler().isRecording();
- }
-
- public void setTalkingState(boolean talking) {
- if (getSessionUser().isSelfMuted() || getSessionUser().isMuted())
- return;
-
- if (mService.getAudioHandler().getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) {
- Log.w(Constants.TAG, "Attempted to set talking state when not using PTT");
- return;
- }
-
- try {
- mService.getAudioHandler().setTalking(talking);
- } catch (AudioException e) {
- mService.logError(e.getMessage());
- }
- }
-
- public void joinChannel(int channel) {
- moveUserToChannel(getSession(), channel);
- }
-
- public void moveUserToChannel(int session, int channel) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSession(session);
- usb.setChannelId(channel);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void createChannel(int parent, String name, String description, int position, boolean temporary) {
- Mumble.ChannelState.Builder csb = Mumble.ChannelState.newBuilder();
- csb.setParent(parent);
- csb.setName(name);
- csb.setDescription(description);
- csb.setPosition(position);
- csb.setTemporary(temporary);
- mService.getConnection().sendTCPMessage(csb.build(), JumbleTCPMessageType.ChannelState);
- }
-
- public void sendAccessTokens(final List<String> tokens) {
- mService.getConnection().sendAccessTokens(tokens);
- }
-
- public void requestBanList() {
- throw new UnsupportedOperationException("Not yet implemented"); // TODO
- }
-
- public void requestUserList() {
- throw new UnsupportedOperationException("Not yet implemented"); // TODO
- }
-
- public void requestPermissions(int channel) {
- Mumble.PermissionQuery.Builder pqb = Mumble.PermissionQuery.newBuilder();
- pqb.setChannelId(channel);
- mService.getConnection().sendTCPMessage(pqb.build(), JumbleTCPMessageType.PermissionQuery);
- }
-
- public void requestComment(int session) {
- Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
- rbb.addSessionComment(session);
- mService.getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
- }
-
- public void requestAvatar(int session) {
- Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
- rbb.addSessionTexture(session);
- mService.getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
- }
-
- public void requestChannelDescription(int channel) {
- Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
- rbb.addChannelDescription(channel);
- mService.getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
- }
-
- public void registerUser(int session) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSession(session);
- usb.setUserId(0);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void kickBanUser(int session, String reason, boolean ban) {
- Mumble.UserRemove.Builder urb = Mumble.UserRemove.newBuilder();
- urb.setSession(session);
- urb.setReason(reason);
- urb.setBan(ban);
- mService.getConnection().sendTCPMessage(urb.build(), JumbleTCPMessageType.UserRemove);
- }
-
- public Message sendUserTextMessage(int session, String message) {
- Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder();
- tmb.addSession(session);
- tmb.setMessage(message);
- mService.getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage);
-
- User self = mService.getModelHandler().getUser(getSession());
- User user = mService.getModelHandler().getUser(session);
- List<User> users = new ArrayList<User>(1);
- users.add(user);
- return new Message(getSession(), self.getName(), new ArrayList<Channel>(0), new ArrayList<Channel>(0), users, message);
- }
-
- public Message sendChannelTextMessage(int channel, String message, boolean tree) {
- Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder();
- if(tree) tmb.addTreeId(channel);
- else tmb.addChannelId(channel);
- tmb.setMessage(message);
- mService.getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage);
-
- User self = mService.getModelHandler().getUser(getSession());
- Channel targetChannel = mService.getModelHandler().getChannel(channel);
- List<Channel> targetChannels = new ArrayList<Channel>();
- targetChannels.add(targetChannel);
- return new Message(getSession(), self.getName(), targetChannels, tree ? targetChannels : new ArrayList<Channel>(0), new ArrayList<User>(0), message);
- }
-
- public void setUserComment(int session, String comment) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSession(session);
- usb.setComment(comment);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void setPrioritySpeaker(int session, boolean priority) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSession(session);
- usb.setPrioritySpeaker(priority);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void removeChannel(int channel) {
- Mumble.ChannelRemove.Builder crb = Mumble.ChannelRemove.newBuilder();
- crb.setChannelId(channel);
- mService.getConnection().sendTCPMessage(crb.build(), JumbleTCPMessageType.ChannelRemove);
- }
-
- public void setMuteDeafState(int session, boolean mute, boolean deaf) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSession(session);
- usb.setMute(mute);
- usb.setDeaf(deaf);
- if (!mute) usb.setSuppress(false);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void setSelfMuteDeafState(boolean mute, boolean deaf) {
- Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
- usb.setSelfMute(mute);
- usb.setSelfDeaf(deaf);
- mService.getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
- }
-
- public void registerObserver(IJumbleObserver observer) {
- mService.registerObserver(observer);
- }
-
- public void unregisterObserver(IJumbleObserver observer) {
- mService.unregisterObserver(observer);
- }
-}
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java
index 4d358f0..127763f 100644
--- a/src/main/java/com/morlunk/jumble/JumbleService.java
+++ b/src/main/java/com/morlunk/jumble/JumbleService.java
@@ -25,6 +25,7 @@ import android.content.IntentFilter;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -35,10 +36,15 @@ import android.util.Log;
import com.morlunk.jumble.audio.AudioOutput;
import com.morlunk.jumble.audio.BluetoothScoReceiver;
import com.morlunk.jumble.exception.AudioException;
+import com.morlunk.jumble.model.Channel;
+import com.morlunk.jumble.model.IChannel;
+import com.morlunk.jumble.model.IUser;
+import com.morlunk.jumble.model.Message;
import com.morlunk.jumble.model.Server;
import com.morlunk.jumble.model.TalkState;
import com.morlunk.jumble.model.User;
import com.morlunk.jumble.net.JumbleConnection;
+import com.morlunk.jumble.net.JumbleUDPMessageType;
import com.morlunk.jumble.util.IJumbleObserver;
import com.morlunk.jumble.util.JumbleException;
import com.morlunk.jumble.net.JumbleTCPMessageType;
@@ -50,9 +56,10 @@ import com.morlunk.jumble.util.JumbleLogger;
import java.security.Security;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.List;
-public class JumbleService extends Service implements JumbleConnection.JumbleConnectionListener, JumbleLogger, BluetoothScoReceiver.Listener {
+public class JumbleService extends Service implements IJumbleService, JumbleConnection.JumbleConnectionListener, JumbleLogger, BluetoothScoReceiver.Listener {
static {
// Use Spongy Castle for crypto implementation so we can create and manage PKCS #12 (.p12) certificates.
@@ -234,7 +241,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
return new JumbleBinder(this);
}
- private void connect() {
+ protected void connect() {
try {
setReconnecting(false);
mConnectionState = ConnectionState.DISCONNECTED;
@@ -576,8 +583,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
* Exposes the current connection. The current connection is set once an attempt to connect to
* a server is made, and remains set until a subsequent connection. It remains available
* after disconnection to provide information regarding the terminated connection.
- * @return The active {@link JumbleConnection}, or null if a connection has not been
- * established yet.
+ * @return The active {@link JumbleConnection}.
*/
public JumbleConnection getConnection() {
return mConnection;
@@ -588,7 +594,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
* to a server, and destroyed upon disconnection.
* @return the active AudioHandler, or null if there is no active connection.
*/
- public AudioHandler getAudioHandler() {
+ private AudioHandler getAudioHandler() {
if (!isSynchronized())
throw new IllegalStateException("Not synchronized");
if (mAudioHandler == null && mConnectionState == ConnectionState.CONNECTED)
@@ -601,7 +607,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
* valid for the lifetime of a connection.
* @return the active ModelHandler, or null if there is no active connection.
*/
- public ModelHandler getModelHandler() {
+ private ModelHandler getModelHandler() {
if (!isSynchronized())
throw new IllegalStateException("Not synchronized");
if (mModelHandler == null && mConnectionState == ConnectionState.CONNECTED)
@@ -614,24 +620,317 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
* @return The {@link BluetoothScoReceiver} attached to this service.
* @throws IllegalStateException if not synchronized or disconnected.
*/
- public BluetoothScoReceiver getBluetoothReceiver() {
+ private BluetoothScoReceiver getBluetoothReceiver() {
if (!isSynchronized())
throw new IllegalStateException("Not synchronized");
return mBluetoothReceiver;
}
+ @Override
+ public JumbleService.ConnectionState getConnectionState() {
+ return mConnectionState;
+ }
+
+ @Override
+ public JumbleException getConnectionError() {
+ JumbleConnection connection = getConnection();
+ return connection != null ? connection.getError() : null;
+ }
+
+ @Override
public boolean isReconnecting() {
return mReconnecting;
}
- public ConnectionState getConnectionState() {
- return mConnectionState;
+ @Override
+ public void cancelReconnect() {
+ setReconnecting(false);
+ }
+
+ @Override
+ public long getTCPLatency() {
+ return getConnection().getTCPLatency();
+ }
+
+ @Override
+ public long getUDPLatency() {
+ return getConnection().getUDPLatency();
+ }
+
+ @Override
+ public int getMaxBandwidth() {
+ return getConnection().getMaxBandwidth();
+ }
+
+ @Override
+ public int getCurrentBandwidth() {
+ return getAudioHandler().getCurrentBandwidth();
+ }
+
+ @Override
+ public int getServerVersion() {
+ return getConnection().getServerVersion();
+ }
+
+ @Override
+ public String getServerRelease() {
+ return getConnection().getServerRelease();
+ }
+
+ @Override
+ public String getServerOSName() {
+ return getConnection().getServerOSName();
+ }
+
+ @Override
+ public String getServerOSVersion() {
+ return getConnection().getServerOSVersion();
+ }
+
+ @Override
+ public int getSession() {
+ return getConnection().getSession();
+ }
+
+ @Override
+ public IUser getSessionUser() {
+ return getModelHandler().getUser(getSession());
+ }
+
+ @Override
+ public IChannel getSessionChannel() {
+ IUser user = getSessionUser();
+ if (user != null)
+ return user.getChannel();
+ throw new IllegalStateException("Session user should be set post-synchronization!");
}
+ @Override
public Server getConnectedServer() {
return mServer;
}
+ @Override
+ public IUser getUser(int session) {
+ return getModelHandler().getUser(session);
+ }
+
+ @Override
+ public IChannel getChannel(int id) {
+ return getModelHandler().getChannel(id);
+ }
+
+ @Override
+ public IChannel getRootChannel() {
+ return getChannel(0);
+ }
+
+ @Override
+ public int getPermissions() {
+ return getModelHandler().getPermissions();
+ }
+
+ @Override
+ public int getTransmitMode() {
+ return getAudioHandler().getTransmitMode();
+ }
+
+ @Override
+ public JumbleUDPMessageType getCodec() {
+ return getConnection().getCodec();
+ }
+
+ @Override
+ public boolean usingBluetoothSco() {
+ return getBluetoothReceiver().isBluetoothScoOn();
+ }
+
+ @Override
+ public void enableBluetoothSco() {
+ getBluetoothReceiver().startBluetoothSco();
+ }
+
+ @Override
+ public void disableBluetoothSco() {
+ getBluetoothReceiver().stopBluetoothSco();
+ }
+
+ @Override
+ public boolean isTalking() {
+ return getAudioHandler().isRecording();
+ }
+
+ @Override
+ public void setTalkingState(boolean talking) {
+ if (getSessionUser().isSelfMuted() || getSessionUser().isMuted())
+ return;
+
+ if (getAudioHandler().getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) {
+ Log.w(Constants.TAG, "Attempted to set talking state when not using PTT");
+ return;
+ }
+
+ try {
+ getAudioHandler().setTalking(talking);
+ } catch (AudioException e) {
+ logError(e.getMessage());
+ }
+ }
+
+ @Override
+ public void joinChannel(int channel) {
+ moveUserToChannel(getSession(), channel);
+ }
+
+ @Override
+ public void moveUserToChannel(int session, int channel) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSession(session);
+ usb.setChannelId(channel);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
+ @Override
+ public void createChannel(int parent, String name, String description, int position, boolean temporary) {
+ Mumble.ChannelState.Builder csb = Mumble.ChannelState.newBuilder();
+ csb.setParent(parent);
+ csb.setName(name);
+ csb.setDescription(description);
+ csb.setPosition(position);
+ csb.setTemporary(temporary);
+ getConnection().sendTCPMessage(csb.build(), JumbleTCPMessageType.ChannelState);
+ }
+
+ @Override
+ public void sendAccessTokens(final List<String> tokens) {
+ getConnection().sendAccessTokens(tokens);
+ }
+
+ @Override
+ public void requestBanList() {
+ throw new UnsupportedOperationException("Not yet implemented"); // TODO
+ }
+
+ @Override
+ public void requestUserList() {
+ throw new UnsupportedOperationException("Not yet implemented"); // TODO
+ }
+
+ @Override
+ public void requestPermissions(int channel) {
+ Mumble.PermissionQuery.Builder pqb = Mumble.PermissionQuery.newBuilder();
+ pqb.setChannelId(channel);
+ getConnection().sendTCPMessage(pqb.build(), JumbleTCPMessageType.PermissionQuery);
+ }
+
+ @Override
+ public void requestComment(int session) {
+ Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
+ rbb.addSessionComment(session);
+ getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
+ }
+
+ @Override
+ public void requestAvatar(int session) {
+ Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
+ rbb.addSessionTexture(session);
+ getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
+ }
+
+ @Override
+ public void requestChannelDescription(int channel) {
+ Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder();
+ rbb.addChannelDescription(channel);
+ getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob);
+ }
+
+ @Override
+ public void registerUser(int session) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSession(session);
+ usb.setUserId(0);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
+ @Override
+ public void kickBanUser(int session, String reason, boolean ban) {
+ Mumble.UserRemove.Builder urb = Mumble.UserRemove.newBuilder();
+ urb.setSession(session);
+ urb.setReason(reason);
+ urb.setBan(ban);
+ getConnection().sendTCPMessage(urb.build(), JumbleTCPMessageType.UserRemove);
+ }
+
+ @Override
+ public Message sendUserTextMessage(int session, String message) {
+ Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder();
+ tmb.addSession(session);
+ tmb.setMessage(message);
+ getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage);
+
+ User self = getModelHandler().getUser(getSession());
+ User user = getModelHandler().getUser(session);
+ List<User> users = new ArrayList<User>(1);
+ users.add(user);
+ return new Message(getSession(), self.getName(), new ArrayList<Channel>(0), new ArrayList<Channel>(0), users, message);
+ }
+
+ @Override
+ public Message sendChannelTextMessage(int channel, String message, boolean tree) {
+ Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder();
+ if(tree) tmb.addTreeId(channel);
+ else tmb.addChannelId(channel);
+ tmb.setMessage(message);
+ getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage);
+
+ User self = getModelHandler().getUser(getSession());
+ Channel targetChannel = getModelHandler().getChannel(channel);
+ List<Channel> targetChannels = new ArrayList<Channel>();
+ targetChannels.add(targetChannel);
+ return new Message(getSession(), self.getName(), targetChannels, tree ? targetChannels : new ArrayList<Channel>(0), new ArrayList<User>(0), message);
+ }
+
+ @Override
+ public void setUserComment(int session, String comment) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSession(session);
+ usb.setComment(comment);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
+ @Override
+ public void setPrioritySpeaker(int session, boolean priority) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSession(session);
+ usb.setPrioritySpeaker(priority);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
+ @Override
+ public void removeChannel(int channel) {
+ Mumble.ChannelRemove.Builder crb = Mumble.ChannelRemove.newBuilder();
+ crb.setChannelId(channel);
+ getConnection().sendTCPMessage(crb.build(), JumbleTCPMessageType.ChannelRemove);
+ }
+
+ @Override
+ public void setMuteDeafState(int session, boolean mute, boolean deaf) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSession(session);
+ usb.setMute(mute);
+ usb.setDeaf(deaf);
+ if (!mute) usb.setSuppress(false);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
+ @Override
+ public void setSelfMuteDeafState(boolean mute, boolean deaf) {
+ Mumble.UserState.Builder usb = Mumble.UserState.newBuilder();
+ usb.setSelfMute(mute);
+ usb.setSelfDeaf(deaf);
+ getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState);
+ }
+
public void registerObserver(IJumbleObserver observer) {
mCallbacks.registerObserver(observer);
}
@@ -640,6 +939,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
mCallbacks.unregisterObserver(observer);
}
+
/**
* The current connection state of the service.
*/
@@ -660,9 +960,21 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
/**
* The connection was lost due to either a kick/ban or socket I/O error.
* Jumble may be reconnecting in this state.
- * @see JumbleBinder#isReconnecting()
- * @see JumbleBinder#cancelReconnect()
+ * @see #isReconnecting()
+ * @see #cancelReconnect()
*/
CONNECTION_LOST
}
+
+ public static class JumbleBinder extends Binder {
+ private final IJumbleService mService;
+
+ private JumbleBinder(IJumbleService service) {
+ mService = service;
+ }
+
+ public IJumbleService getService() {
+ return mService;
+ }
+ }
}