diff options
Diffstat (limited to 'src/main')
16 files changed, 318 insertions, 33 deletions
diff --git a/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl b/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl index 7e0d0fe..874e782 100644 --- a/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl +++ b/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl @@ -29,6 +29,7 @@ interface IJumbleObserver { void onChannelAdded(out Channel channel); void onChannelStateUpdated(out Channel channel); void onChannelRemoved(out Channel channel); + void onChannelPermissionsUpdated(out Channel channel); // User void onUserConnected(out User user); diff --git a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl b/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl index fffade7..7ba3594 100644 --- a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl +++ b/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl @@ -34,6 +34,8 @@ interface IJumbleService { Channel getChannel(int id); List getUserList(); List getChannelList(); + List getLogHistory(); + int getPermissions(); // Audio actions int getTransmitMode(); @@ -49,6 +51,7 @@ interface IJumbleService { void requestBanList(); void requestUserList(); //void requestACL(int channel); + void requestPermissions(int channel); void requestComment(int session); void requestChannelDescription(int channel); void registerUser(int session); @@ -59,6 +62,7 @@ interface IJumbleService { void removeChannel(int channel); //void addChannelLink(int channel, int link); //void requestChannelPermissions(int channel); + void setMuteDeafState(int session, boolean mute, boolean deaf); void setSelfMuteDeafState(boolean mute, boolean deaf); //void announceRecordingState(boolean recording); diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index 6702c42..24138ee 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -36,8 +36,13 @@ import com.morlunk.jumble.net.JumbleTCPMessageType; import com.morlunk.jumble.net.TextMessageHandler; import com.morlunk.jumble.net.UserHandler; import com.morlunk.jumble.protobuf.Mumble; +import com.morlunk.jumble.util.MessageFormatter; import java.security.Security; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; import java.util.List; public class JumbleService extends Service implements JumbleConnection.JumbleConnectionListener { @@ -82,6 +87,12 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon private TextMessageHandler mTextMessageHandler; private AudioOutput mAudioOutput; + // Logging + private List<String> mLogHistory = new ArrayList<String>(); + private SimpleDateFormat mChatDateFormat = new SimpleDateFormat("[h:mm a] "); + + private int mPermissions; + private RemoteCallbackList<IJumbleObserver> mObservers = new RemoteCallbackList<IJumbleObserver>(); /** @@ -147,6 +158,16 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } @Override + public List getLogHistory() throws RemoteException { + return mLogHistory; + } + + @Override + public int getPermissions() throws RemoteException { + return mPermissions; + } + + @Override public int getTransmitMode() throws RemoteException { return mTransmitMode; } @@ -204,6 +225,13 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } @Override + public void requestPermissions(int channel) throws RemoteException { + Mumble.PermissionQuery.Builder pqb = Mumble.PermissionQuery.newBuilder(); + pqb.setChannelId(channel); + mConnection.sendTCPMessage(pqb.build(), JumbleTCPMessageType.PermissionQuery); + } + + @Override public void requestComment(int session) throws RemoteException { Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); rbb.addSessionComment(session); @@ -240,6 +268,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon tmb.addSession(session); tmb.setMessage(message); mConnection.sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); + + // Log message to chat + User target = getUser(session); + String formattedMessage = getString(R.string.chat_message_to, MessageFormatter.highlightString(target.getName()), message); + logMessage(formattedMessage); } @Override @@ -251,6 +284,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon tmb.addChannelId(channel); tmb.setMessage(message); mConnection.sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); + + // Log message to chat + Channel target = getChannel(channel); + String formattedMessage = getString(R.string.chat_message_to, MessageFormatter.highlightString(target.getName()), message); + logMessage(formattedMessage); } @Override @@ -269,6 +307,15 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } @Override + public void setMuteDeafState(int session, boolean mute, boolean deaf) throws RemoteException { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setMute(mute); + usb.setDeaf(deaf); + mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } + + @Override public void setSelfMuteDeafState(boolean mute, boolean deaf) throws RemoteException { Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); usb.setSelfMute(mute); @@ -308,7 +355,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mClientName = extras.containsKey(EXTRAS_CLIENT_NAME) ? extras.getString(EXTRAS_CLIENT_NAME) : "Jumble"; connect(); } - return START_STICKY; + return START_NOT_STICKY; } @Override @@ -328,6 +375,10 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon return mBinder; } + public IJumbleService getBinder() { + return mBinder; + } + public void connect() { try { mConnection = new JumbleConnection(this, this, mServer, mClientName, mCertificate, mCertificatePassword, mForceTcp, mUseOpus, mUseTor); @@ -344,6 +395,9 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon return; } + mLogHistory.clear(); + mPermissions = 0; + mChannelHandler = new ChannelHandler(this); mUserHandler = new UserHandler(this); mTextMessageHandler = new TextMessageHandler(this); @@ -407,6 +461,8 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mChannelHandler = null; mUserHandler = null; + + stopSelf(); } @Override @@ -441,6 +497,10 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon return mChannelHandler; } + public void setPermissions(int permissions) { + mPermissions = permissions; + } + /* * --- HERE BE CALLBACKS --- * This code will be called by components of the service like ChannelHandler. @@ -448,10 +508,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon /** * Logs a warning message to the client. - * @param warning An HTML warning string to be messaged to the client. + * @param warning An HTML warning string to be messagxz ed to the client. */ public void logWarning(final String warning) { Log.w(Constants.TAG, warning); + mLogHistory.add(warning); notifyObservers(new ObserverRunnable() { @Override public void run(IJumbleObserver observer) throws RemoteException { @@ -465,11 +526,16 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon * @param info An HTML info string to be messaged to the client. */ public void logInfo(final String info) { - Log.v(Constants.TAG, info); + if(!mConnection.isSynchronized()) + return; // Don't log messages while synchronizing. + + final String formatInfo = mChatDateFormat.format(new Date())+info; + Log.v(Constants.TAG, formatInfo); + mLogHistory.add(formatInfo); notifyObservers(new ObserverRunnable() { @Override public void run(IJumbleObserver observer) throws RemoteException { - observer.onLogInfo(info); + observer.onLogInfo(formatInfo); } }); } @@ -479,10 +545,12 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon * @param message An HTML message to send to the client. */ public void logMessage(final String message) { + final String formatMessage = mChatDateFormat.format(new Date())+message; + mLogHistory.add(formatMessage); notifyObservers(new ObserverRunnable() { @Override public void run(IJumbleObserver observer) throws RemoteException { - observer.onMessageReceived(message); + observer.onMessageReceived(formatMessage); } }); } diff --git a/src/main/java/com/morlunk/jumble/audio/AudioInput.java b/src/main/java/com/morlunk/jumble/audio/AudioInput.java new file mode 100644 index 0000000..a73d0f3 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/audio/AudioInput.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Andrew Comminos + * + * 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.audio; + +import com.morlunk.jumble.audio.opus.SWIGTYPE_p_OpusEncoder; +import com.morlunk.jumble.net.JumbleUDPMessageType; + +/** + * Created by andrew on 23/08/13. + */ +public class AudioInput { + private SWIGTYPE_p_OpusEncoder mOpusEncoder; + private com.morlunk.jumble.audio.celt11.SWIGTYPE_p_CELTEncoder mCelt11Encoder; + private com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTEncoder mCelt7Encoder; + + private JumbleUDPMessageType mCodec; + + public AudioInput(JumbleUDPMessageType codec) { + mCodec = codec; + switch (codec) { + case UDPVoiceOpus: + break; + case UDPVoiceCELTBeta: + break; + case UDPVoiceCELTAlpha: + break; + case UDPVoiceSpeex: + break; + } + } + + public void startRecording() { + + } + + public void stopRecording() { + + } +} diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java index 830c91c..2691afe 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java @@ -35,6 +35,7 @@ import com.morlunk.jumble.net.PacketDataStream; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -43,6 +44,9 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, AudioOutputSpeech.TalkStateListener { + /** Number of nanoseconds until sleeping audio output thread. */ + private static final long SLEEP_THRESHOLD = 2000000000L; + private JumbleService mService; private ConcurrentHashMap<Integer, AudioOutputSpeech> mAudioOutputs = new ConcurrentHashMap<Integer, AudioOutputSpeech>(); private AudioTrack mAudioTrack; @@ -50,6 +54,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, private Thread mThread; private Object mInactiveLock = new Object(); // Lock that the audio thread waits on when there's no audio to play. Wake when we get a frame. private boolean mRunning = false; + private long mLastPacket; // Time that the last packet was received, in nanoseconds public AudioOutput(JumbleService service) { mService = service; @@ -59,7 +64,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, Audio.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, - bufferSize, + Audio.FRAME_SIZE*12, AudioTrack.MODE_STREAM); } @@ -86,10 +91,11 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, final short[] mix = new short[Audio.FRAME_SIZE*12]; while(mRunning) { + Arrays.fill(mix, (short)0); boolean play = mix(mix, mix.length); if(play) { mAudioTrack.write(mix, 0, mix.length); - } else { + } else if(System.nanoTime()-mLastPacket > SLEEP_THRESHOLD) { Log.v(Constants.TAG, "Pausing audio output thread."); synchronized (mInactiveLock) { try { @@ -131,7 +137,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, } for(AudioOutputSpeech speech : del) - mAudioOutputs.remove(speech); + mAudioOutputs.remove(speech.getSession()); return !mix.isEmpty(); } @@ -167,6 +173,8 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, } aop.addFrameToBuffer(packet.array(), seq); + + mLastPacket = System.nanoTime(); synchronized (mInactiveLock) { mInactiveLock.notify(); } diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java index 8e8ff86..8b866a8 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java @@ -230,29 +230,29 @@ public class AudioOutputSpeech { if(mCodec == JumbleUDPMessageType.UDPVoiceCELTAlpha) { CELT7.celt_decode_float(mCELTAlphaDecoder, - mFrames.isEmpty() ? null : data, + data, data.length, out); } else if(mCodec == JumbleUDPMessageType.UDPVoiceCELTBeta) { CELT11.celt_decode_float(mCELTBetaDecoder, - mFrames.isEmpty() ? null : data, + data, data.length, out, Audio.FRAME_SIZE); } else if(mCodec == JumbleUDPMessageType.UDPVoiceOpus) { decodedSamples = Opus.opus_decode_float(mOpusDecoder, - mFrames.isEmpty() ? null : data, + data, data.length, out, mAudioBufferSize, 0); } else { // Speex - if(mFrames.isEmpty()) - Speex.speex_decode(mSpeexDecoder, null, out); - else { +// if(data.isEmpty()) +// Speex.speex_decode(mSpeexDecoder, null, out); +// else { Speex.speex_bits_read_from(mSpeexBits, data, data.length); Speex.speex_decode(mSpeexDecoder, mSpeexBits, out); - } +// } for(int i = 0; i < Audio.FRAME_SIZE; i++) out[i] *= (1.0f / 32767.f); } @@ -307,6 +307,7 @@ public class AudioOutputSpeech { talkState = User.TalkState.WHISPERING; break; } + mTalkStateListener.onTalkStateUpdated(mSession, talkState); boolean tmp = mLastAlive; @@ -333,6 +334,10 @@ public class AudioOutputSpeech { return mCodec; } + public int getSession() { + return mSession; + } + /** * Cleans up all JNI refs linked to this instance. * This MUST be called eventually, otherwise we get memory leaks! diff --git a/src/main/java/com/morlunk/jumble/model/Channel.java b/src/main/java/com/morlunk/jumble/model/Channel.java index cedf236..2350918 100644 --- a/src/main/java/com/morlunk/jumble/model/Channel.java +++ b/src/main/java/com/morlunk/jumble/model/Channel.java @@ -36,6 +36,7 @@ public final class Channel implements Parcelable { private List<Integer> mUsers = new ArrayList<Integer>(); private List<Integer> mLinks = new ArrayList<Integer>(); private int mUserCount; + private int mPermissions; public static final Parcelable.Creator<Channel> CREATOR = new Parcelable.Creator<Channel>() { @@ -77,6 +78,7 @@ public final class Channel implements Parcelable { out.writeList(mUsers); out.writeList(mLinks); out.writeInt(mUserCount); + out.writeInt(mPermissions); } public void readFromParcel(Parcel in) { @@ -93,6 +95,7 @@ public final class Channel implements Parcelable { mUsers = in.readArrayList(null); mLinks = in.readArrayList(null); mUserCount = in.readInt(); + mPermissions = in.readInt(); } @Override @@ -206,4 +209,12 @@ public final class Channel implements Parcelable { public void setSubchannelUserCount(int userCount) { mUserCount = userCount; } + + public int getPermissions() { + return mPermissions; + } + + public void setPermissions(int permissions) { + mPermissions = permissions; + } } diff --git a/src/main/java/com/morlunk/jumble/model/User.java b/src/main/java/com/morlunk/jumble/model/User.java index fa3e38d..e95b726 100644 --- a/src/main/java/com/morlunk/jumble/model/User.java +++ b/src/main/java/com/morlunk/jumble/model/User.java @@ -19,6 +19,8 @@ package com.morlunk.jumble.model; import android.os.Parcel; import android.os.Parcelable; +import com.google.protobuf.ByteString; + public class User implements Parcelable { public static enum TalkState { @@ -29,10 +31,10 @@ public class User implements Parcelable { } private int mSession; - private int mId; + private int mId = -1; private String mName; private String mComment; - private byte[] mCommentHash; + private ByteString mCommentHash; private String mHash; private boolean mMuted; @@ -85,8 +87,8 @@ public class User implements Parcelable { out.writeInt(mId); out.writeString(mName); out.writeString(mComment); - out.writeInt(mCommentHash.length); - out.writeByteArray(mCommentHash); + out.writeInt(mCommentHash.size()); + out.writeByteArray(mCommentHash.toByteArray()); out.writeString(mHash); out.writeValue(mMuted); out.writeValue(mDeafened); @@ -105,8 +107,9 @@ public class User implements Parcelable { mId = in.readInt(); mName = in.readString(); mComment = in.readString(); - mCommentHash = new byte[in.readInt()]; - in.readByteArray(mCommentHash); + byte[] commentHash = new byte[in.readInt()]; + in.readByteArray(commentHash); + mCommentHash = ByteString.copyFrom(commentHash); mHash = in.readString(); mMuted = (Boolean)in.readValue(null); mDeafened = (Boolean)in.readValue(null); @@ -161,12 +164,12 @@ public class User implements Parcelable { this.mComment = mComment; } - public byte[] getCommentHash() { + public ByteString getCommentHash() { return mCommentHash; } - public void setCommentHash(byte[] mCommentHash) { - this.mCommentHash = mCommentHash; + public void setCommentHash(ByteString commentHash) { + mCommentHash = commentHash; } public String getHash() { diff --git a/src/main/java/com/morlunk/jumble/net/ChannelHandler.java b/src/main/java/com/morlunk/jumble/net/ChannelHandler.java index ff897d7..2366d70 100644 --- a/src/main/java/com/morlunk/jumble/net/ChannelHandler.java +++ b/src/main/java/com/morlunk/jumble/net/ChannelHandler.java @@ -159,4 +159,24 @@ public class ChannelHandler extends JumbleMessageHandler.Stub { }); } } + + @Override + public void messagePermissionQuery(Mumble.PermissionQuery msg) { + if(msg.getFlush()) + for(Channel channel : mChannels.values()) + channel.setPermissions(0); + + final Channel channel = mChannels.get(msg.getChannelId()); + if(channel != null) { + channel.setPermissions(msg.getPermissions()); + if(msg.getChannelId() == 0) // If we're provided permissions for the root channel, we'll apply these as our server permissions. + mService.setPermissions(msg.getPermissions()); + mService.notifyObservers(new JumbleService.ObserverRunnable() { + @Override + public void run(IJumbleObserver observer) throws RemoteException { + observer.onChannelPermissionsUpdated(channel); + } + }); + } + } } diff --git a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java index b507eba..37ea33e 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java @@ -87,6 +87,7 @@ public class JumbleConnection { private boolean mUseTor; private boolean mUsingUDP = true; private boolean mConnected; + private boolean mSynchronized; private CryptState mCryptState = new CryptState(); private long mStartTimestamp; // Time that the connection was initiated in nanoseconds @@ -121,6 +122,7 @@ public class JumbleConnection { mPingExecutorService.scheduleAtFixedRate(mPingRunnable, 0, 5, TimeUnit.SECONDS); mSession = msg.getSession(); + mSynchronized = true; mMainHandler.post(new Runnable() { @Override @@ -146,7 +148,7 @@ public class JumbleConnection { @Override public void messageUserRemove(final Mumble.UserRemove msg) { - if(msg.getActor() == mSession) { + if(msg.getSession() == mSession) { if(mListener != null) { mMainHandler.post(new Runnable() { @Override @@ -292,6 +294,7 @@ public class JumbleConnection { public void connect() { mConnected = false; + mSynchronized = false; mUsingUDP = !mForceTCP; mStartTimestamp = System.nanoTime(); @@ -309,6 +312,15 @@ public class JumbleConnection { return mConnected; } + /** + * Returns whether or not the service is fully synchronized with the remote server- this happens when we get the ServerSync message. + * You shouldn't log any user actions until the connection is synchronized. + * @return true or false, depending on whether or not we have received the ServerSync message. + */ + public boolean isSynchronized() { + return mSynchronized; + } + public long getElapsed() { return (System.nanoTime()-mStartTimestamp)/1000; } @@ -346,6 +358,7 @@ public class JumbleConnection { */ public void disconnect() { mConnected = false; + mSynchronized = false; mNetworkHandler.postAtFrontOfQueue(new Runnable() { @Override public void run() { @@ -368,6 +381,7 @@ public class JumbleConnection { */ public void forceDisconnect() { mConnected = false; + mSynchronized = false; mExecutorService.shutdownNow(); mPingExecutorService.shutdownNow(); mTCP = null; diff --git a/src/main/java/com/morlunk/jumble/net/JumbleObserver.java b/src/main/java/com/morlunk/jumble/net/JumbleObserver.java index b95ec11..865b124 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleObserver.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleObserver.java @@ -58,6 +58,11 @@ public class JumbleObserver extends IJumbleObserver.Stub { } @Override + public void onChannelPermissionsUpdated(Channel channel) throws RemoteException { + + } + + @Override public void onUserConnected(User user) throws RemoteException { } diff --git a/src/main/java/com/morlunk/jumble/net/Permissions.java b/src/main/java/com/morlunk/jumble/net/Permissions.java new file mode 100644 index 0000000..561ff9b --- /dev/null +++ b/src/main/java/com/morlunk/jumble/net/Permissions.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 Andrew Comminos + * + * 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.net; + +/** + * Created by andrew on 21/08/13. + */ +public class Permissions { + public static final int None = 0x0, + Write = 0x1, + Traverse = 0x2, + Enter = 0x4, + Speak = 0x8, + MuteDeafen = 0x10, + Move = 0x20, + MakeChannel = 0x40, + LinkChannel = 0x80, + Whisper = 0x100, + TextMessage = 0x200, + MakeTempChannel = 0x400, + + // Root channel only + Kick = 0x10000, + Ban = 0x20000, + Register = 0x40000, + SelfRegister = 0x80000, + + Cached = 0x8000000, + All = 0xf07ff; +} diff --git a/src/main/java/com/morlunk/jumble/net/TextMessageHandler.java b/src/main/java/com/morlunk/jumble/net/TextMessageHandler.java index bda7a53..5b63667 100644 --- a/src/main/java/com/morlunk/jumble/net/TextMessageHandler.java +++ b/src/main/java/com/morlunk/jumble/net/TextMessageHandler.java @@ -19,6 +19,7 @@ package com.morlunk.jumble.net; import com.morlunk.jumble.JumbleService; import com.morlunk.jumble.model.User; import com.morlunk.jumble.protobuf.Mumble; +import com.morlunk.jumble.util.MessageFormatter; /** * Handles receiving text messages. @@ -41,7 +42,7 @@ public class TextMessageHandler extends JumbleMessageHandler.Stub { return; // TODO use more localized strings here - String senderName = sender != null ? sender.getName() : "Server"; + String senderName = sender != null ? MessageFormatter.highlightString(sender.getName()) : "Server"; String senderTarget = ""; if(msg.getTreeIdCount() > 0) diff --git a/src/main/java/com/morlunk/jumble/net/UserHandler.java b/src/main/java/com/morlunk/jumble/net/UserHandler.java index 908c27d..ee2e404 100644 --- a/src/main/java/com/morlunk/jumble/net/UserHandler.java +++ b/src/main/java/com/morlunk/jumble/net/UserHandler.java @@ -26,6 +26,7 @@ import com.morlunk.jumble.R; import com.morlunk.jumble.model.Channel; import com.morlunk.jumble.model.User; import com.morlunk.jumble.protobuf.Mumble; +import com.morlunk.jumble.util.MessageFormatter; import java.util.ArrayList; import java.util.Collections; @@ -89,6 +90,12 @@ public class UserHandler extends JumbleMessageHandler.Stub { user = new User(msg.getSession(), msg.getName()); mUsers.put(msg.getSession(), user); newUser = true; + // Add user to root channel by default. This works because for some reason, we don't get a channel ID when the user joins into root. + Channel root = mService.getChannelHandler().getChannel(0); + user.setChannelId(0); + root.addUser(user.getSession()); + root.setSubchannelUserCount(root.getSubchannelUserCount()+1); + sortUsers(root); } else return; @@ -110,7 +117,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { } if(newUser) - mService.logInfo(mService.getString(R.string.chat_notify_connected, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_connected, MessageFormatter.highlightString(user.getName()))); if(msg.hasSelfDeaf() || msg.hasSelfMute()) { if(msg.hasSelfMute()) @@ -120,11 +127,11 @@ public class UserHandler extends JumbleMessageHandler.Stub { if(self != null && user.getSession() != self.getSession() && (user.getChannelId() == self.getChannelId())) { if(user.isSelfMuted() && user.isSelfDeafened()) - mService.logInfo(mService.getString(R.string.chat_notify_now_muted_deafened, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_now_muted_deafened, MessageFormatter.highlightString(user.getName()))); else if(user.isSelfMuted()) - mService.logInfo(mService.getString(R.string.chat_notify_now_muted, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_now_muted, MessageFormatter.highlightString(user.getName()))); else - mService.logInfo(mService.getString(R.string.chat_notify_now_unmuted, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_now_unmuted, MessageFormatter.highlightString(user.getName()))); } } @@ -142,9 +149,9 @@ public class UserHandler extends JumbleMessageHandler.Stub { // If in a linked channel OR the same channel as the current user, notify the user about recording if(selfChannel != null && (selfChannel.getLinks().contains(user.getChannelId()) || self.getChannelId() == user.getChannelId())) { if(user.isRecording()) - mService.logInfo(mService.getString(R.string.chat_notify_user_recording_started, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_user_recording_started, MessageFormatter.highlightString(user.getName()))); else - mService.logInfo(mService.getString(R.string.chat_notify_user_recording_stopped, user.getName())); + mService.logInfo(mService.getString(R.string.chat_notify_user_recording_stopped, MessageFormatter.highlightString(user.getName()))); } } } @@ -203,7 +210,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { */ if(msg.hasCommentHash()) - user.setCommentHash(msg.getCommentHash().toByteArray()); + user.setCommentHash(msg.getCommentHash()); if(msg.hasComment()) user.setComment(msg.getComment()); diff --git a/src/main/java/com/morlunk/jumble/util/MessageFormatter.java b/src/main/java/com/morlunk/jumble/util/MessageFormatter.java new file mode 100644 index 0000000..58c15cb --- /dev/null +++ b/src/main/java/com/morlunk/jumble/util/MessageFormatter.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Andrew Comminos + * + * 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.util; + +/** + * Formats strings into HTML. + * Created by andrew on 24/08/13. + */ +public class MessageFormatter { + + public static final String HIGHLIGHT_COLOR = "33b5e5"; + + private static final String HTML_FONT_COLOR_FORMAT = "<font color=\"#%s\">%s</font>"; + + /** + * Highlights the passed string using the service's defined color {@link MessageFormatter#HIGHLIGHT_COLOR}. + * @param string The string to highlight. + * @return The passed string enclosed with HTML font tags specifying the color. + */ + public static String highlightString(String string) { + return String.format(HTML_FONT_COLOR_FORMAT, HIGHLIGHT_COLOR, string); + } +} diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e68149e..bae8628 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <string name="chat_message_to">To %1$s: %2$s</string> <string name="chat_notify_moved">%1$s moved in from %2$s by %3$s.</string> <string name="chat_notify_muted_deafened">Muted and deafened.</string> <string name="chat_notify_muted">Muted.</string> |