From f8c63977be9fc4bd08e0db5dec2ac4a637069864 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Tue, 12 Jan 2016 03:09:18 +0100 Subject: [PATCH] Added a lot of chat view related code --- app/src/main/AndroidManifest.xml | 2 +- .../de/kuschku/libquassel/BusProvider.java | 3 - .../java/de/kuschku/libquassel/Client.java | 4 +- .../de/kuschku/libquassel/CoreConnection.java | 7 +- .../backlogmanagers/BacklogManager.java | 4 +- .../backlogmanagers/SimpleBacklogManager.java | 16 +- .../events/BacklogReceivedEvent.java | 9 + .../libquassel/localtypes/ChannelBuffer.java | 2 +- .../libquassel/localtypes/QueryBuffer.java | 2 +- .../libquassel/localtypes/StatusBuffer.java | 2 +- .../types => message}/Message.java | 3 +- .../primitives/QMetaTypeRegistry.java | 2 +- .../serializers/MessageSerializer.java | 2 +- .../ClientBackgroundThread.java | 5 +- .../quasseldroid_ng/QuasselService.java | 2 +- .../{ => ui}/BufferDrawerItem.java | 3 +- .../ui/ChatMessageRenderer.java | 322 +++++++++++++++++ .../{ => ui}/MainActivity.java | 329 ++++++++++-------- .../quasseldroid_ng/ui/MessageAdapter.java | 50 +++ .../quasseldroid_ng/ui/MessageViewHolder.java | 22 ++ .../{ => ui}/NetworkDrawerItem.java | 2 +- .../util/CompatibilityUtils.java | 13 + .../util/DateFormatHelper.java | 18 + .../quasseldroid_ng/util/IrcFormatHelper.java | 31 ++ .../util/IrcUserUtils.java | 11 +- .../{utils => util}/ServerAddress.java | 2 +- .../quasseldroid_ng/util/SpanFormatter.java | 113 ++++++ .../quasseldroid_ng/util/ThemeUtil.java | 120 +++++++ .../java/de/kuschku/util/ObservableList.java | 8 + app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/content_main.xml | 12 +- app/src/main/res/layout/core_dialog.xml | 6 +- app/src/main/res/layout/login_dialog.xml | 6 +- .../main/res/layout/widget_chatmessage.xml | 26 ++ app/src/main/res/values/attrs.xml | 55 ++- app/src/main/res/values/strings.xml | 27 ++ app/src/main/res/values/styles.xml | 7 +- app/src/main/res/values/themes.xml | 95 +++++ build.gradle | 2 +- 39 files changed, 1166 insertions(+), 181 deletions(-) create mode 100644 app/src/main/java/de/kuschku/libquassel/events/BacklogReceivedEvent.java rename app/src/main/java/de/kuschku/libquassel/{primitives/types => message}/Message.java (98%) rename app/src/main/java/de/kuschku/quasseldroid_ng/{ => ui}/BufferDrawerItem.java (92%) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java rename app/src/main/java/de/kuschku/quasseldroid_ng/{ => ui}/MainActivity.java (53%) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java rename app/src/main/java/de/kuschku/quasseldroid_ng/{ => ui}/NetworkDrawerItem.java (96%) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java rename app/src/main/java/de/kuschku/{ => quasseldroid_ng}/util/IrcUserUtils.java (89%) rename app/src/main/java/de/kuschku/quasseldroid_ng/{utils => util}/ServerAddress.java (82%) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java create mode 100644 app/src/main/res/layout/widget_chatmessage.xml create mode 100644 app/src/main/res/values/themes.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0417563f7..cff36a31b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,7 +12,7 @@ <service android:name=".QuasselService" /> <activity - android:name=".MainActivity" + android:name=".ui.MainActivity" android:label="@string/app_name" android:launchMode="singleTop"> <intent-filter> diff --git a/app/src/main/java/de/kuschku/libquassel/BusProvider.java b/app/src/main/java/de/kuschku/libquassel/BusProvider.java index f4848ec5a..b351a039d 100644 --- a/app/src/main/java/de/kuschku/libquassel/BusProvider.java +++ b/app/src/main/java/de/kuschku/libquassel/BusProvider.java @@ -18,17 +18,14 @@ public class BusProvider { } public void handle(Object o) { - //Log.d("HANDLE", o.getClass().getSimpleName()); this.handle.post(o); } public void dispatch(Object o) { - //Log.d("DISPATCH", o.getClass().getSimpleName()); this.dispatch.post(o); } public void sendEvent(Object o) { - //Log.d("EVENT", o.getClass().getSimpleName()); this.event.post(o); } } diff --git a/app/src/main/java/de/kuschku/libquassel/Client.java b/app/src/main/java/de/kuschku/libquassel/Client.java index 199c761f0..5d01acbcf 100644 --- a/app/src/main/java/de/kuschku/libquassel/Client.java +++ b/app/src/main/java/de/kuschku/libquassel/Client.java @@ -17,7 +17,7 @@ import de.kuschku.libquassel.localtypes.Buffers; import de.kuschku.libquassel.objects.types.ClientInitAck; import de.kuschku.libquassel.objects.types.SessionState; import de.kuschku.libquassel.primitives.types.BufferInfo; -import de.kuschku.libquassel.primitives.types.Message; +import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.primitives.types.QVariant; import de.kuschku.libquassel.syncables.types.BufferSyncer; import de.kuschku.libquassel.syncables.types.BufferViewManager; @@ -33,6 +33,8 @@ public class Client { private final BacklogManager backlogManager; private final BusProvider busProvider; public int lag; + public int openBuffer; + public int openBufferView; private ConnectionChangeEvent.Status connectionStatus; private ClientInitAck core; private SessionState state; diff --git a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java index 1755f261e..0a4970bf0 100644 --- a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java +++ b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java @@ -24,7 +24,7 @@ import de.kuschku.libquassel.primitives.types.Protocol; import de.kuschku.libquassel.protocols.DatastreamPeer; import de.kuschku.libquassel.protocols.LegacyPeer; import de.kuschku.libquassel.protocols.RemotePeer; -import de.kuschku.quasseldroid_ng.utils.ServerAddress; +import de.kuschku.quasseldroid_ng.util.ServerAddress; import de.kuschku.util.niohelpers.WrappedChannel; import static de.kuschku.libquassel.primitives.QMetaType.Type.UInt; @@ -62,11 +62,12 @@ public class CoreConnection { * This method opens a socket to the specified address and starts the connection process. * * @throws IOException + * @param supportsKeepAlive */ - public void open() throws IOException { + public void open(boolean supportsKeepAlive) throws IOException { // Intialize socket socket = new Socket(); - socket.setKeepAlive(true); + if (supportsKeepAlive) socket.setKeepAlive(true); socket.connect(new InetSocketAddress(address.host, address.port), 10000); // Wrap socket in channel for nio functions diff --git a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java index a3a742338..7cd7ad5b1 100644 --- a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java +++ b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/BacklogManager.java @@ -5,7 +5,7 @@ import android.support.v7.widget.RecyclerView; import java.util.List; -import de.kuschku.libquassel.primitives.types.Message; +import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.syncables.types.SyncableObject; import de.kuschku.util.ObservableList; @@ -19,4 +19,6 @@ public abstract class BacklogManager extends SyncableObject { public abstract ObservableList<Message> get(int bufferId); public abstract void bind(int bufferId, @Nullable RecyclerView.Adapter adapter); + + public abstract void requestMoreBacklog(int bufferId, int count); } diff --git a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java index 1b2ca5f85..df96be405 100644 --- a/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java +++ b/app/src/main/java/de/kuschku/libquassel/backlogmanagers/SimpleBacklogManager.java @@ -10,9 +10,10 @@ import java.util.List; import de.kuschku.libquassel.BusProvider; import de.kuschku.libquassel.Client; +import de.kuschku.libquassel.events.BacklogReceivedEvent; import de.kuschku.libquassel.functions.types.InitDataFunction; import de.kuschku.libquassel.functions.types.SyncFunction; -import de.kuschku.libquassel.primitives.types.Message; +import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.primitives.types.QVariant; import de.kuschku.util.ObservableList; @@ -36,6 +37,8 @@ public class SimpleBacklogManager extends BacklogManager { public void receiveBacklog(int bufferId, int from, int to, int count, int extra, List<Message> messages) { get(bufferId).list.addAll(messages); + + busProvider.sendEvent(new BacklogReceivedEvent(bufferId)); } @Override @@ -50,6 +53,17 @@ public class SimpleBacklogManager extends BacklogManager { get(bufferId).setCallback(new ObservableList.RecyclerViewAdapterCallback(adapter)); } + @Override + public void requestMoreBacklog(int bufferId, int count) { + ObservableList<Message> backlog = backlogs.get(bufferId); + int messageId = + (backlog == null) ? -1 : + (backlog.first() == null) ? -1 : + backlog.first().messageId; + + requestBacklog(bufferId, -1, -1, count, 0); + } + public ObservableList<Message> get(int bufferId) { if (backlogs.get(bufferId) == null) backlogs.put(bufferId, new ObservableList<>(Message.class)); diff --git a/app/src/main/java/de/kuschku/libquassel/events/BacklogReceivedEvent.java b/app/src/main/java/de/kuschku/libquassel/events/BacklogReceivedEvent.java new file mode 100644 index 000000000..b28fa6cd3 --- /dev/null +++ b/app/src/main/java/de/kuschku/libquassel/events/BacklogReceivedEvent.java @@ -0,0 +1,9 @@ +package de.kuschku.libquassel.events; + +public class BacklogReceivedEvent { + public final int bufferId; + + public BacklogReceivedEvent(int bufferId) { + this.bufferId = bufferId; + } +} diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java index 0f50962d3..96d986a21 100644 --- a/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java +++ b/app/src/main/java/de/kuschku/libquassel/localtypes/ChannelBuffer.java @@ -4,7 +4,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import de.kuschku.libquassel.primitives.types.BufferInfo; import de.kuschku.libquassel.syncables.types.IrcChannel; -import de.kuschku.quasseldroid_ng.BufferDrawerItem; +import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem; public class ChannelBuffer implements Buffer { private final BufferInfo info; diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java index df5ad5268..676aa546a 100644 --- a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java +++ b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java @@ -4,7 +4,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import de.kuschku.libquassel.primitives.types.BufferInfo; import de.kuschku.libquassel.syncables.types.IrcUser; -import de.kuschku.quasseldroid_ng.BufferDrawerItem; +import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem; public class QueryBuffer implements Buffer { private final BufferInfo info; diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java index 51a08e1e8..807402540 100644 --- a/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java +++ b/app/src/main/java/de/kuschku/libquassel/localtypes/StatusBuffer.java @@ -4,7 +4,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import de.kuschku.libquassel.primitives.types.BufferInfo; import de.kuschku.libquassel.syncables.types.Network; -import de.kuschku.quasseldroid_ng.BufferDrawerItem; +import de.kuschku.quasseldroid_ng.ui.BufferDrawerItem; public class StatusBuffer implements Buffer { private final BufferInfo info; diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/types/Message.java b/app/src/main/java/de/kuschku/libquassel/message/Message.java similarity index 98% rename from app/src/main/java/de/kuschku/libquassel/primitives/types/Message.java rename to app/src/main/java/de/kuschku/libquassel/message/Message.java index cb68f51a8..b3a80ef6c 100644 --- a/app/src/main/java/de/kuschku/libquassel/primitives/types/Message.java +++ b/app/src/main/java/de/kuschku/libquassel/message/Message.java @@ -1,4 +1,4 @@ -package de.kuschku.libquassel.primitives.types; +package de.kuschku.libquassel.message; import android.support.annotation.NonNull; @@ -7,6 +7,7 @@ import org.joda.time.DateTime; import java.io.Serializable; import java.util.Comparator; +import de.kuschku.libquassel.primitives.types.BufferInfo; import de.kuschku.util.ContentComparable; public class Message implements ContentComparable<Message> { diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java b/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java index 7219a30b5..27c87e26d 100644 --- a/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java +++ b/app/src/main/java/de/kuschku/libquassel/primitives/QMetaTypeRegistry.java @@ -33,7 +33,7 @@ import de.kuschku.libquassel.primitives.serializers.VariantSerializer; import de.kuschku.libquassel.primitives.serializers.VariantVariantListSerializer; import de.kuschku.libquassel.primitives.serializers.VoidSerializer; import de.kuschku.libquassel.primitives.types.BufferInfo; -import de.kuschku.libquassel.primitives.types.Message; +import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.primitives.types.QVariant; import de.kuschku.libquassel.syncables.serializers.IdentitySerializer; import de.kuschku.libquassel.syncables.types.Identity; diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java index eb0e07d29..6dffba067 100644 --- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java +++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; -import de.kuschku.libquassel.primitives.types.Message; +import de.kuschku.libquassel.message.Message; public class MessageSerializer implements PrimitiveSerializer<Message> { @Override diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java index 1cd423884..981a3e435 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ClientBackgroundThread.java @@ -8,7 +8,8 @@ import de.kuschku.libquassel.CoreConnection; import de.kuschku.libquassel.ProtocolHandler; import de.kuschku.libquassel.events.GeneralErrorEvent; import de.kuschku.libquassel.protocols.RemotePeer; -import de.kuschku.quasseldroid_ng.utils.ServerAddress; +import de.kuschku.quasseldroid_ng.util.CompatibilityUtils; +import de.kuschku.quasseldroid_ng.util.ServerAddress; public class ClientBackgroundThread implements Runnable { public static final ClientData CLIENT_DATA = new ClientData( @@ -33,7 +34,7 @@ public class ClientBackgroundThread implements Runnable { @Override public void run() { try { - connection.open(); + connection.open(!CompatibilityUtils.isChromiumDevice()); } catch (IOException e) { provider.sendEvent(new GeneralErrorEvent(e)); } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java b/app/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java index 49006e9c8..22dda9dda 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/QuasselService.java @@ -6,7 +6,7 @@ import android.os.Binder; import android.os.IBinder; import de.kuschku.libquassel.BusProvider; -import de.kuschku.quasseldroid_ng.utils.ServerAddress; +import de.kuschku.quasseldroid_ng.util.ServerAddress; public class QuasselService extends Service { private final IBinder binder = new LocalBinder(); diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/BufferDrawerItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java similarity index 92% rename from app/src/main/java/de/kuschku/quasseldroid_ng/BufferDrawerItem.java rename to app/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java index c891932d9..2df5843fe 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/BufferDrawerItem.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/BufferDrawerItem.java @@ -1,4 +1,4 @@ -package de.kuschku.quasseldroid_ng; +package de.kuschku.quasseldroid_ng.ui; import com.mikepenz.materialdrawer.holder.ImageHolder; import com.mikepenz.materialdrawer.holder.StringHolder; @@ -6,6 +6,7 @@ import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; import de.kuschku.libquassel.localtypes.Buffer; import de.kuschku.libquassel.localtypes.ChannelBuffer; +import de.kuschku.quasseldroid_ng.R; public class BufferDrawerItem extends SecondaryDrawerItem { final Buffer buffer; diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java new file mode 100644 index 000000000..272b26960 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatMessageRenderer.java @@ -0,0 +1,322 @@ +package de.kuschku.quasseldroid_ng.ui; + +import android.content.Context; +import android.text.SpannableString; +import android.text.TextUtils; + +import org.joda.time.format.DateTimeFormatter; + +import java.util.Locale; + +import de.kuschku.libquassel.Client; +import de.kuschku.libquassel.message.Message; +import de.kuschku.quasseldroid_ng.R; +import de.kuschku.quasseldroid_ng.util.DateFormatHelper; +import de.kuschku.quasseldroid_ng.util.IrcFormatHelper; +import de.kuschku.quasseldroid_ng.util.IrcUserUtils; +import de.kuschku.quasseldroid_ng.util.SpanFormatter; +import de.kuschku.quasseldroid_ng.util.ThemeUtil; + +public class ChatMessageRenderer { + private final ThemeUtil themeUtil; + private final DateTimeFormatter format; + private final FormatStrings strings; + private final IrcFormatHelper helper; + + private Client client; + private boolean fullHostmask = false; + + public ChatMessageRenderer(Context ctx) { + this.themeUtil = new ThemeUtil(ctx); + this.format = DateFormatHelper.getTimeFormatter(ctx); + this.strings = new FormatStrings(ctx); + this.helper = new IrcFormatHelper(themeUtil.colors); + } + + public void setClient(Client client) { + this.client = client; + } + + private void setColors(MessageViewHolder holder, boolean highlight) { + if (highlight) { + holder.content.setTextColor(themeUtil.colors.colorForegroundHighlight); + holder.time.setTextColor(themeUtil.colors.colorForegroundHighlight); + holder.itemView.setBackgroundColor(themeUtil.colors.colorBackgroundHighlight); + } else { + holder.content.setTextColor(themeUtil.colors.colorForeground); + holder.time.setTextColor(themeUtil.colors.colorForegroundSecondary); + holder.itemView.setBackgroundColor(themeUtil.colors.transparent); + } + } + + private CharSequence formatNick(String hostmask, boolean full) { + CharSequence formattedNick = helper.formatUserNick(IrcUserUtils.getNick(hostmask)); + if (full) { + return strings.formatUsername(formattedNick, IrcUserUtils.getMask(hostmask)); + } else { + return formattedNick; + } + } + + private CharSequence formatNick(String hostmask) { + return formatNick(hostmask, fullHostmask); + } + + public void onBindPlain(MessageViewHolder holder, Message message) { + holder.content.setText( + strings.formatPlain( + formatNick(message.sender, false), + helper.formatIrcMessage(message.content) + ) + ); + } + + public void onBindNotice(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindAction(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindNick(MessageViewHolder holder, Message message) { + if (message.flags.Self) + holder.content.setText(strings.formatNick( + formatNick(message.sender, false) + )); + else + holder.content.setText(strings.formatNick( + formatNick(message.sender, false), + helper.formatUserNick(message.content) + )); + } + + public void onBindMode(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindJoin(MessageViewHolder holder, Message message) { + holder.content.setText(strings.formatJoin( + formatNick(message.sender), + client.getBuffer(message.bufferInfo.id).getName() + )); + } + + public void onBindPart(MessageViewHolder holder, Message message) { + holder.content.setText(strings.formatPart( + formatNick(message.sender), + client.getBuffer(message.bufferInfo.id).getName(), + message.content + )); + } + + public void onBindQuit(MessageViewHolder holder, Message message) { + holder.content.setText(strings.formatQuit( + formatNick(message.sender), + message.content + )); + } + + public void onBindKick(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindKill(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindServer(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindInfo(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindError(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindDayChange(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindTopic(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindNetsplitJoin(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindNetsplitQuit(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBindInvite(MessageViewHolder holder, Message message) { + holder.content.setText(message.toString()); + } + + public void onBind(MessageViewHolder holder, Message message) { + setColors(holder, message.flags.Highlight); + holder.time.setText(format.print(message.time)); + switch (message.type) { + case Plain: + onBindPlain(holder, message); + break; + case Notice: + onBindNotice(holder, message); + break; + case Action: + onBindAction(holder, message); + break; + case Nick: + onBindNick(holder, message); + break; + case Mode: + onBindMode(holder, message); + break; + case Join: + onBindJoin(holder, message); + break; + case Part: + onBindPart(holder, message); + break; + case Quit: + onBindQuit(holder, message); + break; + case Kick: + onBindKick(holder, message); + break; + case Kill: + onBindKill(holder, message); + break; + case Server: + onBindServer(holder, message); + break; + case Info: + onBindInfo(holder, message); + break; + case Error: + onBindError(holder, message); + break; + case DayChange: + onBindDayChange(holder, message); + break; + case Topic: + onBindTopic(holder, message); + break; + case NetsplitJoin: + onBindNetsplitJoin(holder, message); + break; + case NetsplitQuit: + onBindNetsplitQuit(holder, message); + break; + case Invite: + onBindInvite(holder, message); + break; + } + } + + private static class FormatStrings { + private final String username_hostmask; + + private final String message_plain; + private final String message_join; + private final String message_part; + private final String message_part_extra; + private final String message_quit; + private final String message_quit_extra; + private final String message_kill; + private final String message_kick; + private final String message_kick_extra; + private final String message_mode; + private final String message_nick_self; + private final String message_nick_other; + private final String message_daychange; + private final String message_action; + + public FormatStrings(Context ctx) { + username_hostmask = ctx.getString(R.string.username_hostmask); + + message_plain = ctx.getString(R.string.message_plain); + message_join = ctx.getString(R.string.message_join); + message_part = ctx.getString(R.string.message_part); + message_part_extra = ctx.getString(R.string.message_part_extra); + message_quit = ctx.getString(R.string.message_quit); + message_quit_extra = ctx.getString(R.string.message_quit_extra); + message_kill = ctx.getString(R.string.message_kill); + message_kick = ctx.getString(R.string.message_kick); + message_kick_extra = ctx.getString(R.string.message_kick_extra); + message_mode = ctx.getString(R.string.message_mode); + message_nick_self = ctx.getString(R.string.message_nick_self); + message_nick_other = ctx.getString(R.string.message_nick_other); + message_daychange = ctx.getString(R.string.message_daychange); + message_action = ctx.getString(R.string.message_action); + } + + public CharSequence formatUsername(CharSequence nick, CharSequence hostmask) { + return SpanFormatter.format(username_hostmask, nick, hostmask); + } + + public CharSequence formatJoin(CharSequence user, CharSequence channel) { + return SpanFormatter.format(message_join, user, channel); + } + + public CharSequence formatPart(CharSequence user, CharSequence channel) { + return SpanFormatter.format(message_part, user, channel); + } + public CharSequence formatPart(CharSequence user, CharSequence channel, CharSequence reason) { + if (reason == null || reason.length() == 0) return formatPart(user, channel); + + return SpanFormatter.format(message_part_extra, user, channel, reason); + } + + public CharSequence formatQuit(CharSequence user) { + return SpanFormatter.format(message_quit, user); + } + public CharSequence formatQuit(CharSequence user, CharSequence reason) { + if (reason == null || reason.length() == 0) return formatQuit(user); + + return SpanFormatter.format(message_quit_extra, user, reason); + } + + public CharSequence formatKill(CharSequence user, CharSequence channel) { + return SpanFormatter.format(message_kill, user, channel); + } + + public CharSequence formatKick(CharSequence user, CharSequence kicked) { + return SpanFormatter.format(message_kick, user, kicked); + } + public CharSequence formatKick(CharSequence user, CharSequence kicked, CharSequence reason) { + if (reason == null || reason.length() == 0) return formatKick(user, kicked); + + return SpanFormatter.format(message_kick_extra, user, kicked, reason); + } + + public CharSequence formatMode(CharSequence mode, CharSequence user) { + return SpanFormatter.format(message_mode, mode, user); + } + + public CharSequence formatNick(CharSequence newNick) { + return SpanFormatter.format(message_nick_self, newNick); + } + public CharSequence formatNick(CharSequence oldNick, CharSequence newNick) { + if (newNick == null || newNick.length() == 0) return formatNick(oldNick); + + return SpanFormatter.format(message_nick_other, oldNick, newNick); + } + + public CharSequence formatDayChange(CharSequence day) { + return SpanFormatter.format(message_daychange, day); + } + + public CharSequence formatAction(CharSequence user, CharSequence channel) { + return SpanFormatter.format(message_action, user, channel); + } + + public CharSequence formatPlain(CharSequence nick, CharSequence message) { + return SpanFormatter.format(message_plain, nick, message); + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/MainActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java similarity index 53% rename from app/src/main/java/de/kuschku/quasseldroid_ng/MainActivity.java rename to app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java index b0d5681f5..eb4bd70b8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/MainActivity.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.java @@ -1,30 +1,26 @@ -package de.kuschku.quasseldroid_ng; +package de.kuschku.quasseldroid_ng.ui; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.res.Configuration; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.support.design.widget.Snackbar; -import android.support.v4.text.TextUtilsCompat; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.AppCompatImageButton; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.style.ForegroundColorSpan; import android.util.Log; -import android.view.LayoutInflater; +import android.view.KeyEvent; import android.view.View; -import android.view.ViewGroup; import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; import com.google.common.collect.Sets; @@ -39,17 +35,17 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IProfile; import com.mikepenz.materialdrawer.util.KeyboardUtil; -import org.joda.time.format.DateTimeFormat; - -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import butterknife.Bind; import butterknife.ButterKnife; import de.kuschku.libquassel.BusProvider; +import de.kuschku.libquassel.Client; import de.kuschku.libquassel.IProtocolHandler; +import de.kuschku.libquassel.events.BacklogReceivedEvent; import de.kuschku.libquassel.events.ConnectionChangeEvent; import de.kuschku.libquassel.events.GeneralErrorEvent; import de.kuschku.libquassel.events.StatusMessageEvent; @@ -57,15 +53,27 @@ import de.kuschku.libquassel.exceptions.UnknownTypeException; import de.kuschku.libquassel.functions.types.HandshakeFunction; import de.kuschku.libquassel.localtypes.Buffer; import de.kuschku.libquassel.objects.types.ClientLogin; -import de.kuschku.libquassel.primitives.types.Message; import de.kuschku.libquassel.syncables.types.BufferViewConfig; +import de.kuschku.libquassel.syncables.types.BufferViewManager; import de.kuschku.libquassel.syncables.types.Network; -import de.kuschku.quasseldroid_ng.utils.ServerAddress; -import de.kuschku.util.IrcUserUtils; -import de.kuschku.util.ObservableList; +import de.kuschku.quasseldroid_ng.BufferViewManagerChangedEvent; +import de.kuschku.quasseldroid_ng.BuildConfig; +import de.kuschku.quasseldroid_ng.QuasselService; +import de.kuschku.quasseldroid_ng.R; +import de.kuschku.quasseldroid_ng.util.CompatibilityUtils; +import de.kuschku.quasseldroid_ng.util.ServerAddress; import de.kuschku.util.backports.Stream; public class MainActivity extends AppCompatActivity { + private static final String BUFFER_ID = "BUFFER_ID"; + private static final String BUFFER_VIEW_ID = "BUFFER_VIEW_ID"; + + private static final String KEY_HOST = "beta_hostname"; + private static final String KEY_PORT = "beta_port"; + private static final String KEY_USER = "beta_username"; + private static final String KEY_PASS = "beta_password"; + + SharedPreferences pref; @Bind(R.id.toolbar) Toolbar toolbar; @@ -79,9 +87,12 @@ public class MainActivity extends AppCompatActivity { @Bind(R.id.send) AppCompatImageButton send; + @Bind(R.id.swipeview) + SwipeRefreshLayout swipeView; + Drawer drawer; AccountHeader header; - MessageAdapter adapter = new MessageAdapter(); + MessageAdapter adapter; QuasselService.LocalBinder binder; @@ -90,7 +101,20 @@ public class MainActivity extends AppCompatActivity { if (service instanceof QuasselService.LocalBinder) { MainActivity.this.binder = (QuasselService.LocalBinder) service; if (binder.getBackgroundThread() != null) { + handler = binder.getBackgroundThread().handler; + toolbar.setSubtitle(binder.getBackgroundThread().connection.getStatus().name()); + if (bufferId != -1) switchBuffer(bufferId); + if (bufferViewId != -1) switchBufferView(bufferViewId); + + // Horrible hack to load bufferviews back, should use ObservableList + Client client = handler == null ? null : handler.getClient(); + BufferViewManager bufferViewManager = client == null ? null : client.getBufferViewManager(); + Map<Integer, BufferViewConfig> bufferViews = bufferViewManager == null ? null : bufferViewManager.BufferViews; + if (bufferViews != null) + for (int id : bufferViews.keySet()) { + onEventMainThread(new BufferViewManagerChangedEvent(id, BufferViewManagerChangedEvent.Action.ADD)); + } } } } @@ -101,11 +125,14 @@ public class MainActivity extends AppCompatActivity { private IProtocolHandler handler; private int bufferId; + private int bufferViewId; + private BusProvider provider; @Override protected void onCreate(Bundle savedInstanceState) { - //setTheme(R.style.AppTheme); - setTheme(R.style.AppTheme_Light); + + // TODO: ADD THEME SELECTION + setTheme(R.style.Quassel); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); @@ -113,6 +140,9 @@ public class MainActivity extends AppCompatActivity { setSupportActionBar(toolbar); + pref = getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE); + adapter = new MessageAdapter(this); + // This fixes a horrible bug android has where opening the keyboard doesn’t resize the layout KeyboardUtil keyboardUtil = new KeyboardUtil(this, findViewById(R.id.layout)); keyboardUtil.enable(); @@ -126,34 +156,9 @@ public class MainActivity extends AppCompatActivity { .withSavedInstance(savedInstanceState) .withCompactStyle(true) .withProfileImagesVisible(false) + // TODO: REWRITE THIS .withOnAccountHeaderListener((view, profile, current) -> { - BufferViewConfig config = handler.getClient().getBufferViewManager().BufferViews.get(profile.getIdentifier()); - ArrayList<IDrawerItem> items = new ArrayList<>(); - if (config.getNetworkId() == 0) { - items.addAll( - new Stream<>(handler.getClient().getNetworks()) - .map(network -> new NetworkDrawerItem(network, - Sets.intersection(network.getBuffers(), new HashSet<>( - new Stream<>(config.getBufferList()) - .map(handler.getClient()::getBuffer) - .list() - )))) - .list() - ); - } else { - Network network = handler.getClient().getNetwork(config.getNetworkId()); - items.add(new NetworkDrawerItem(network, - Sets.intersection(network.getBuffers(), new HashSet<>( - new Stream<>(config.getBufferList()) - .map(handler.getClient()::getBuffer) - .list() - )) - )); - } - drawer.setItems(items); - for (int i = 0; i < drawer.getAdapter().getItemCount(); i++) { - drawer.getAdapter().open(i); - } + switchBufferView(profile.getIdentifier()); return true; }) .build(); @@ -162,22 +167,34 @@ public class MainActivity extends AppCompatActivity { .withActivity(this) .withToolbar(toolbar) .withAccountHeader(header) + // TODO: REWRITE THIS .withOnDrawerItemClickListener((view, position, drawerItem) -> { if (drawerItem != null) { if (position == -1) { binder.stopBackgroundThread(); View coreview = View.inflate(this, R.layout.core_dialog, null); + EditText hostname = ((EditText) coreview.findViewById(R.id.server)); + EditText port = ((EditText) coreview.findViewById(R.id.port)); + + hostname.setText(pref.getString(KEY_HOST, "")); + port.setText(String.valueOf(pref.getInt(KEY_PORT, 4242))); new AlertDialog.Builder(this) .setView(coreview) .setPositiveButton("Connect", (dialog, which) -> { - EditText hostname = ((EditText) coreview.findViewById(R.id.server)); - EditText port = ((EditText) coreview.findViewById(R.id.port)); - if (binder.getBackgroundThread() != null) - binder.getBackgroundThread().provider.event.unregister(this); + if (provider != null) provider.event.unregister(this); binder.stopBackgroundThread(); - BusProvider provider = new BusProvider(); + provider = new BusProvider(); provider.event.register(this); - binder.startBackgroundThread(provider, new ServerAddress(hostname.getText().toString().trim(), Integer.valueOf(port.getText().toString().trim()))); + + String value_hostname = hostname.getText().toString().trim(); + Integer value_port = Integer.valueOf(port.getText().toString().trim()); + + SharedPreferences.Editor edit = pref.edit(); + edit.putString(KEY_HOST, value_hostname); + edit.putInt(KEY_PORT, value_port); + edit.commit(); + + binder.startBackgroundThread(provider, new ServerAddress(value_hostname, value_port)); handler = binder.getBackgroundThread().handler; }) .setNegativeButton("Cancel", (dialog, which) -> { @@ -200,23 +217,89 @@ public class MainActivity extends AppCompatActivity { .withShowDrawerOnFirstLaunch(true) .build(); + // TODO: REWRITE THIS + if (CompatibilityUtils.isChromiumDevice()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary, getTheme())); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary)); + } + } + drawer.addStickyFooterItem(new PrimaryDrawerItem().withName("(Re-)Connect").withIcon(R.drawable.ic_server_light)); messages.setAdapter(adapter); messages.setLayoutManager(new LinearLayoutManager(this)); + swipeView.setOnRefreshListener(() -> { + if (handler != null) handler.getClient().getBacklogManager().requestMoreBacklog(bufferId, 20); + else swipeView.setRefreshing(false); + }); + + send.setOnClickListener(view -> { + sendInput(); + }); + chatline.setOnKeyListener((v, keyCode, event) -> { + if (event.getAction() == KeyEvent.ACTION_DOWN && (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER)) + sendInput(); - send.setOnClickListener((view) -> { - Buffer buffer = handler.getClient().getBuffer(bufferId); - handler.getClient().sendInput(buffer.getInfo(), chatline.getText().toString()); - chatline.setText(""); + return false; }); } @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(BUFFER_ID, bufferId); + outState.putInt(BUFFER_VIEW_ID, bufferViewId); + drawer.saveInstanceState(outState); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState != null) { + bufferId = savedInstanceState.getInt(BUFFER_ID, -1); + bufferViewId = savedInstanceState.getInt(BUFFER_VIEW_ID, -1); + } } + // TODO: USE OBSERVABLELIST FOR THIS + private void switchBufferView(int bufferviewId) { + this.bufferViewId = bufferviewId; + adapter.setClient(handler.getClient()); + BufferViewConfig config = handler.getClient().getBufferViewManager().BufferViews.get(bufferviewId); + ArrayList<IDrawerItem> items = new ArrayList<>(); + if (config != null) { + if (config.getNetworkId() == 0) { + items.addAll( + new Stream<>(handler.getClient().getNetworks()) + .map(network -> new NetworkDrawerItem(network, + Sets.intersection(network.getBuffers(), new HashSet<>( + new Stream<>(config.getBufferList()) + .map(handler.getClient()::getBuffer) + .list() + )))) + .list() + ); + } else { + Network network = handler.getClient().getNetwork(config.getNetworkId()); + items.add(new NetworkDrawerItem(network, + Sets.intersection(network.getBuffers(), new HashSet<>( + new Stream<>(config.getBufferList()) + .map(handler.getClient()::getBuffer) + .list() + )) + )); + } + } + drawer.setItems(items); + for (int i = 0; i < drawer.getAdapter().getItemCount(); i++) { + drawer.getAdapter().open(i); + } + } + + + // TODO: REWRITE THIS private void switchBuffer(int bufferId) { this.bufferId = bufferId; @@ -232,7 +315,27 @@ public class MainActivity extends AppCompatActivity { drawer.closeDrawer(); } - ; + // TODO: REWRITE THIS + private void sendInput() { + Buffer buffer = null; + Client client = null; + if (handler != null) client = handler.getClient(); + if (client != null) buffer = client.getBuffer(bufferId); + + String str = chatline.getText().toString(); + if (buffer != null && !str.isEmpty()) handler.getClient().sendInput(buffer.getInfo(), str); + chatline.setText(""); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + @Override + protected void onResume() { + super.onResume(); + } @Override protected void onDestroy() { @@ -240,21 +343,38 @@ public class MainActivity extends AppCompatActivity { unbindService(serviceConnection); } + // TODO: REWRITE THIS public void onEventMainThread(ConnectionChangeEvent event) { switch (event.status) { case DISCONNECTED: binder.stopBackgroundThread(); break; case CONNECTED: + // TODO: COMMENT THIS + System.gc(); + if (bufferViewId == -1 && header.getProfiles().size() > 0) + switchBufferView(header.getProfiles().get(0).getIdentifier()); break; case LOGIN_REQUIRED: View loginview = View.inflate(this, R.layout.login_dialog, null); + EditText username = ((EditText) loginview.findViewById(R.id.username)); + EditText password = ((EditText) loginview.findViewById(R.id.password)); + username.setText(pref.getString(KEY_USER, "")); + password.setText(pref.getString(KEY_PASS, "")); new AlertDialog.Builder(this) .setView(loginview) .setPositiveButton("Login", (dialog, which) -> { + String value_user = username.getText().toString(); + String value_pass = password.getText().toString(); + + SharedPreferences.Editor edit = pref.edit(); + edit.putString(KEY_USER, value_user); + edit.putString(KEY_PASS, value_pass); + edit.commit(); + binder.getBackgroundThread().provider.dispatch(new HandshakeFunction(new ClientLogin( - ((EditText) loginview.findViewById(R.id.username)).getText().toString(), - ((EditText) loginview.findViewById(R.id.password)).getText().toString() + value_user, + value_pass ))); }) .setNegativeButton("Cancel", (dialog, which) -> { @@ -268,9 +388,9 @@ public class MainActivity extends AppCompatActivity { toolbar.setSubtitle(event.status.name()); } + // TODO: USE OBSERVABLE LIST FOR THIS SHIT public void onEventMainThread(BufferViewManagerChangedEvent event) { IProfile activeProfile = header.getActiveProfile(); - int selectedProfile = activeProfile == null ? -1 : activeProfile.getIdentifier(); switch (event.action) { case ADD: BufferViewConfig add = handler.getClient().getBufferViewManager().BufferViews.get(event.id); @@ -292,92 +412,29 @@ public class MainActivity extends AppCompatActivity { break; } Collections.sort(header.getProfiles(), (x, y) -> x.getIdentifier() - y.getIdentifier()); - if (event.action == BufferViewManagerChangedEvent.Action.REMOVE && event.id == selectedProfile) { + if (event.action == BufferViewManagerChangedEvent.Action.REMOVE && event.id == bufferViewId) { ArrayList<IProfile> profiles = header.getProfiles(); if (!profiles.isEmpty()) header.setActiveProfile(profiles.get(0), true); - } else if (event.action == BufferViewManagerChangedEvent.Action.MODIFY && event.id == selectedProfile) { - header.setActiveProfile(selectedProfile, true); + } else if (event.action == BufferViewManagerChangedEvent.Action.MODIFY && event.id == bufferViewId) { + header.setActiveProfile(bufferViewId, true); } } + public void onEventMainThread(BacklogReceivedEvent event) { + if (event.bufferId == bufferId) swipeView.setRefreshing(false); + } + + // TODO: REWRITE THIS public void onEventMainThread(StatusMessageEvent event) { Toast.makeText(this, String.format("%s: %s", event.scope, event.message), Toast.LENGTH_LONG).show(); } + // TODO: REWRITE THIS public void onEventMainThread(GeneralErrorEvent event) { if (event.exception != null && !(event.exception instanceof UnknownTypeException)) { - Log.e("libquassel", event.toString()); + event.exception.printStackTrace(); Snackbar.make(messages, event.toString(), Snackbar.LENGTH_LONG).show(); } } - - class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> { - ObservableList<Message> messageList = new ObservableList<>(Message.class); - - public void setMessageList(ObservableList<Message> messageList) { - this.messageList.setCallback(null); - this.messageList = messageList; - this.messageList.setCallback(new ObservableList.RecyclerViewAdapterCallback(this)); - notifyDataSetChanged(); - } - - @Override - public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new MessageViewHolder(LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1, parent, false)); - } - - @Override - public void onBindViewHolder(MessageViewHolder holder, int position) { - int[] colors = { - R.color.md_pink_500, - R.color.md_purple_500, - R.color.md_red_500, - R.color.md_green_500, - R.color.md_cyan_500, - R.color.md_deep_purple_500, - R.color.md_amber_500, - R.color.md_blue_500, - R.color.md_pink_700, - R.color.md_purple_700, - R.color.md_red_700, - R.color.md_green_700, - R.color.md_cyan_700, - R.color.md_deep_purple_700, - R.color.md_amber_700, - R.color.md_blue_700 - }; - - Message msg = messageList.list.get(position); - SpannableString timeSpan = new SpannableString(DateTimeFormat.forPattern("[hh:mm]").print(msg.time)); - timeSpan.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.md_light_secondary)), 0, timeSpan.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - - String nick = IrcUserUtils.getNick(msg.sender); - SpannableString nickSpan = new SpannableString(nick); - nickSpan.setSpan(new ForegroundColorSpan(getResources().getColor(colors[IrcUserUtils.getSenderColor(nick)])), 0, nickSpan.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - - holder.text1.setText(TextUtils.concat( - timeSpan, - " ", - nickSpan, - " ", - msg.content - )); - } - - @Override - public int getItemCount() { - return messageList.list.size(); - } - } - - class MessageViewHolder extends RecyclerView.ViewHolder { - @Bind(android.R.id.text1) - TextView text1; - - public MessageViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - } - } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java new file mode 100644 index 000000000..c95fac8ab --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageAdapter.java @@ -0,0 +1,50 @@ +package de.kuschku.quasseldroid_ng.ui; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import de.kuschku.libquassel.Client; +import de.kuschku.libquassel.message.Message; +import de.kuschku.quasseldroid_ng.R; +import de.kuschku.util.ObservableList; + +public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> { + private ObservableList<Message> messageList = new ObservableList<>(Message.class); + private ChatMessageRenderer renderer; + private LayoutInflater inflater; + + public MessageAdapter(Context ctx) { + this.inflater = LayoutInflater.from(ctx); + this.renderer = new ChatMessageRenderer(ctx); + } + + public void setClient(Client client) { + renderer.setClient(client); + } + + public void setMessageList(ObservableList<Message> messageList) { + this.messageList.setCallback(null); + this.messageList = messageList; + this.messageList.setCallback(new ObservableList.RecyclerViewAdapterCallback(this)); + notifyDataSetChanged(); + } + + @Override + public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new MessageViewHolder(inflater.inflate(R.layout.widget_chatmessage, parent, false)); + } + + // TODO: REWRITE THIS + @Override + public void onBindViewHolder(MessageViewHolder holder, int position) { + Message msg = messageList.list.get(position); + renderer.onBind(holder, msg); + } + + @Override + public int getItemCount() { + return messageList.list.size(); + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java new file mode 100644 index 000000000..bd7131ad9 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MessageViewHolder.java @@ -0,0 +1,22 @@ +package de.kuschku.quasseldroid_ng.ui; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import butterknife.Bind; +import butterknife.ButterKnife; +import de.kuschku.quasseldroid_ng.R; + +class MessageViewHolder extends RecyclerView.ViewHolder { + @Bind(R.id.time) + TextView time; + + @Bind(R.id.content) + TextView content; + + public MessageViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/NetworkDrawerItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java similarity index 96% rename from app/src/main/java/de/kuschku/quasseldroid_ng/NetworkDrawerItem.java rename to app/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java index 5def317fb..4368fc4df 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/NetworkDrawerItem.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/NetworkDrawerItem.java @@ -1,4 +1,4 @@ -package de.kuschku.quasseldroid_ng; +package de.kuschku.quasseldroid_ng.ui; import com.mikepenz.materialdrawer.holder.StringHolder; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java new file mode 100644 index 000000000..8a3c9ea7c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/CompatibilityUtils.java @@ -0,0 +1,13 @@ +package de.kuschku.quasseldroid_ng.util; + +import android.os.Build; + +public class CompatibilityUtils { + private CompatibilityUtils() { + + } + + public static boolean isChromiumDevice() { + return (Build.MANUFACTURER.toLowerCase().contains("chromium") && Build.BRAND.toLowerCase().contains("chromium")); + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java new file mode 100644 index 000000000..f5620c835 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/DateFormatHelper.java @@ -0,0 +1,18 @@ +package de.kuschku.quasseldroid_ng.util; + +import android.content.Context; + +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.text.SimpleDateFormat; + +public class DateFormatHelper { + private DateFormatHelper() { + + } + + public static DateTimeFormatter getTimeFormatter(Context ctx) { + return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getTimeFormat(ctx)).toLocalizedPattern()); + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java new file mode 100644 index 000000000..51e506ee7 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcFormatHelper.java @@ -0,0 +1,31 @@ +package de.kuschku.quasseldroid_ng.util; + + +import android.graphics.Typeface; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.text.style.StyleSpan; + +public class IrcFormatHelper { + private final ThemeUtil.Colors colors; + + public IrcFormatHelper(ThemeUtil.Colors colors) { + this.colors = colors; + } + + public CharSequence formatUserNick(String nick) { + int colorIndex = IrcUserUtils.getSenderColor(nick); + int color = colors.senderColors[colorIndex]; + + SpannableString str = new SpannableString(nick); + str.setSpan(new ForegroundColorSpan(color), 0, nick.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + str.setSpan(new StyleSpan(Typeface.BOLD), 0, nick.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return str; + } + + public CharSequence formatIrcMessage(String message) { + SpannableString str = new SpannableString(message); + return str; + } +} diff --git a/app/src/main/java/de/kuschku/util/IrcUserUtils.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java similarity index 89% rename from app/src/main/java/de/kuschku/util/IrcUserUtils.java rename to app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java index 03953b10a..608a35c84 100644 --- a/app/src/main/java/de/kuschku/util/IrcUserUtils.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/IrcUserUtils.java @@ -1,4 +1,4 @@ -package de.kuschku.util; +package de.kuschku.quasseldroid_ng.util; import java.nio.charset.Charset; import java.util.Locale; @@ -70,10 +70,15 @@ public class IrcUserUtils { } public static String getUser(String hostmask) { - return hostmask.split("!")[1].split("@")[0]; + return getMask(hostmask).split("@")[0]; } public static String getHost(String hostmask) { - return hostmask.split("@")[1]; + return getMask(hostmask).split("@")[1]; + } + + + public static String getMask(String hostmask) { + return hostmask.split("!")[1]; } } \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/utils/ServerAddress.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java similarity index 82% rename from app/src/main/java/de/kuschku/quasseldroid_ng/utils/ServerAddress.java rename to app/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java index 74dc99443..20b2059ea 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/utils/ServerAddress.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ServerAddress.java @@ -1,4 +1,4 @@ -package de.kuschku.quasseldroid_ng.utils; +package de.kuschku.quasseldroid_ng.util; public class ServerAddress { public final String host; diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java new file mode 100644 index 000000000..71c1ba9cb --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/SpanFormatter.java @@ -0,0 +1,113 @@ +/* +* Copyright © 2014 George T. Steel +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package de.kuschku.quasseldroid_ng.util; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.SpannedString; +import android.text.Spannable; + +/** + * Provides {@link String#format} style functions that work with {@link Spanned} strings and preserve formatting. + * + * @author George T. Steel + * + */ +public class SpanFormatter { + public static final Pattern FORMAT_SEQUENCE = Pattern.compile("%([0-9]+\\$|<?)([^a-zA-z%]*)([[a-zA-Z%]&&[^tT]]|[tT][a-zA-Z])"); + + private SpanFormatter(){} + + /** + * Version of {@link String#format(String, Object...)} that works on {@link Spanned} strings to preserve rich text formatting. + * Both the {@code format} as well as any {@code %s args} can be Spanned and will have their formatting preserved. + * Due to the way {@link Spannable}s work, any argument's spans will can only be included <b>once</b> in the result. + * Any duplicates will appear as text only. + * + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + * @return the formatted string (with spans). + */ + public static SpannedString format(CharSequence format, Object... args) { + return format(Locale.getDefault(), format, args); + } + + /** + * Version of {@link String#format(Locale, String, Object...)} that works on {@link Spanned} strings to preserve rich text formatting. + * Both the {@code format} as well as any {@code %s args} can be Spanned and will have their formatting preserved. + * Due to the way {@link Spannable}s work, any argument's spans will can only be included <b>once</b> in the result. + * Any duplicates will appear as text only. + * + * @param locale + * the locale to apply; {@code null} value means no localization. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. + * @return the formatted string (with spans). + * @see String#format(Locale, String, Object...) + */ + public static SpannedString format(Locale locale, CharSequence format, Object... args){ + SpannableStringBuilder out = new SpannableStringBuilder(format); + + int i = 0; + int argAt = -1; + + while (i < out.length()){ + Matcher m = FORMAT_SEQUENCE.matcher(out); + if (!m.find(i)) break; + i=m.start(); + int exprEnd = m.end(); + + String argTerm = m.group(1); + String modTerm = m.group(2); + String typeTerm = m.group(3); + + CharSequence cookedArg; + + if (typeTerm.equals("%")){ + cookedArg = "%"; + }else if (typeTerm.equals("%")){ + cookedArg = "\n"; + }else{ + int argIdx = 0; + if (argTerm.equals("")) argIdx = ++argAt; + else if (argTerm.equals("<")) argIdx = argAt; + else argIdx = Integer.parseInt(argTerm.substring(0, argTerm.length() - 1)) -1; + + Object argItem = args[argIdx]; + + if (typeTerm.equals("s") && argItem instanceof Spanned){ + cookedArg = (Spanned) argItem; + }else{ + cookedArg = String.format(locale, "%"+modTerm+typeTerm, argItem); + } + } + + out.replace(i, exprEnd, cookedArg); + i += cookedArg.length(); + } + + return new SpannedString(out); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java new file mode 100644 index 000000000..10927d499 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ThemeUtil.java @@ -0,0 +1,120 @@ +package de.kuschku.quasseldroid_ng.util; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.support.annotation.ColorInt; + +import de.kuschku.quasseldroid_ng.R; + +public class ThemeUtil { + public final Colors colors = new Colors(); + + private static final int[] ATTRS_SENDER = { + // sender colors + R.attr.senderColor0, + R.attr.senderColor1, + R.attr.senderColor2, + R.attr.senderColor3, + R.attr.senderColor4, + R.attr.senderColor5, + R.attr.senderColor6, + R.attr.senderColor7, + R.attr.senderColor8, + R.attr.senderColor9, + R.attr.senderColorA, + R.attr.senderColorB, + R.attr.senderColorC, + R.attr.senderColorD, + R.attr.senderColorE, + R.attr.senderColorF, + }; + + private static final int[] ATTRS_MIRC = { + // mirc colors + R.attr.mircColor0, + R.attr.mircColor1, + R.attr.mircColor2, + R.attr.mircColor3, + R.attr.mircColor4, + R.attr.mircColor5, + R.attr.mircColor6, + R.attr.mircColor7, + R.attr.mircColor8, + R.attr.mircColor9, + R.attr.mircColorA, + R.attr.mircColorB, + R.attr.mircColorC, + R.attr.mircColorD, + R.attr.mircColorE, + R.attr.mircColorF, + }; + + private static final int[] ATTRS_GENERAL = { + // General UI colors + R.attr.colorForeground, + R.attr.colorForegroundHighlight, + R.attr.colorForegroundSecondary, + + R.attr.colorBackground, + R.attr.colorBackgroundHighlight, + R.attr.colorBackgroundCard, + }; + + private static final int[] ATTRS_TINT = { + // Tint colors + R.attr.colorTintActivity, + R.attr.colorTintMessage, + R.attr.colorTintHighlight + }; + + public ThemeUtil(Context ctx) { + initColors(ctx.getTheme()); + } + + public void initColors(Resources.Theme theme) { + TypedArray arr; + + arr = theme.obtainStyledAttributes(ATTRS_SENDER); + for (int i = 0; i < colors.senderColors.length;i++) { + colors.senderColors[i] = arr.getColor(i, colors.transparent); + } + arr.recycle(); + + arr = theme.obtainStyledAttributes(ATTRS_MIRC); + for (int i = 0; i < colors.senderColors.length;i++) { + colors.mircColors[i] = arr.getColor(i, colors.transparent); + } + arr.recycle(); + + arr = theme.obtainStyledAttributes(ATTRS_GENERAL); + colors.colorForeground = arr.getColor(0, colors.transparent); + colors.colorForegroundHighlight = arr.getColor(1, colors.transparent); + colors.colorForegroundSecondary = arr.getColor(2, colors.transparent); + colors.colorBackground = arr.getColor(3, colors.transparent); + colors.colorBackgroundHighlight = arr.getColor(4, colors.transparent); + colors.colorBackgroundCard = arr.getColor(5, colors.transparent); + arr.recycle(); + + arr = theme.obtainStyledAttributes(ATTRS_TINT); + colors.colorTintActivity = arr.getColor(0, colors.transparent); + colors.colorTintMessage = arr.getColor(1, colors.transparent); + colors.colorTintHighlight = arr.getColor(2, colors.transparent); + arr.recycle(); + } + + public static class Colors { + @ColorInt public int transparent = 0x00000000; + @ColorInt public int[] senderColors = new int[16]; + @ColorInt public int[] mircColors = new int[16]; + @ColorInt public int colorForeground = 0x00000000; + @ColorInt public int colorForegroundHighlight = 0x00000000; + @ColorInt public int colorForegroundSecondary = 0x00000000; + @ColorInt public int colorBackground = 0x00000000; + @ColorInt public int colorBackgroundHighlight = 0x00000000; + @ColorInt public int colorBackgroundCard = 0x00000000; + @ColorInt public int colorTintActivity = 0x00000000; + @ColorInt public int colorTintMessage = 0x00000000; + @ColorInt public int colorTintHighlight = 0x00000000; + } +} diff --git a/app/src/main/java/de/kuschku/util/ObservableList.java b/app/src/main/java/de/kuschku/util/ObservableList.java index d28aa0d86..b3c37813f 100644 --- a/app/src/main/java/de/kuschku/util/ObservableList.java +++ b/app/src/main/java/de/kuschku/util/ObservableList.java @@ -22,9 +22,17 @@ public class ObservableList<T extends ContentComparable<T>> { } public T last() { + if (list.size() == 0) return null; + return list.get(list.size() - 1); } + public T first() { + if (list.size() == 0) return null; + + return list.get(0); + } + public interface UICallback { void notifyItemInserted(int position); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 003b52b4d..b117d1bdf 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/layout" - tools:context="de.kuschku.quasseldroid_ng.MainActivity" + tools:context=".ui.MainActivity" android:orientation="vertical" > <android.support.design.widget.AppBarLayout diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 9a1f5707a..db9f019c4 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -5,13 +5,17 @@ android:layout_width="match_parent" android:layout_height="match_parent" tools:showIn="@layout/activity_main"> - - <android.support.v7.widget.RecyclerView - android:id="@+id/messages" + <android.support.v4.widget.SwipeRefreshLayout + android:id="@+id/swipeview" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" - android:background="?attr/messagesBackground" /> + android:background="?attr/colorBackground" > + <android.support.v7.widget.RecyclerView + android:id="@+id/messages" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </android.support.v4.widget.SwipeRefreshLayout> <View android:layout_width="fill_parent" diff --git a/app/src/main/res/layout/core_dialog.xml b/app/src/main/res/layout/core_dialog.xml index a91c3a2e7..36cdf9966 100644 --- a/app/src/main/res/layout/core_dialog.xml +++ b/app/src/main/res/layout/core_dialog.xml @@ -7,14 +7,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/server" - android:inputType="textUri" - android:text="192.168.178.241"/> + android:inputType="textUri" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/port" - android:inputType="number" - android:text="@string/default_port" /> + android:inputType="number" /> </LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/login_dialog.xml b/app/src/main/res/layout/login_dialog.xml index 000b67993..de8c9ad13 100644 --- a/app/src/main/res/layout/login_dialog.xml +++ b/app/src/main/res/layout/login_dialog.xml @@ -6,14 +6,12 @@ <EditText android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/username" - android:text="test"/> + android:id="@+id/username" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/password" - android:inputType="textPassword" - android:text="password"/> + android:inputType="textPassword" /> </LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_chatmessage.xml b/app/src/main/res/layout/widget_chatmessage.xml new file mode 100644 index 000000000..4b17f7deb --- /dev/null +++ b/app/src/main/res/layout/widget_chatmessage.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textAppearance="?android:attr/textAppearanceListItemSmall" + android:gravity="center_vertical" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:minHeight="?android:attr/listPreferredItemHeightSmall" > + <TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:typeface="monospace" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" /> + <TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/content" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" /> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 3868f1a86..2e9ab05cb 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,7 +1,60 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <attr name="iconServer" format="reference" /> <attr name="messagesBackground" format="reference" /> <attr name="chatlineBackground" format="reference" /> <attr name="dividerColor" format="reference"/> + + <!-- sender colors --> + + <attr name="senderColor0" format="color" /> + <attr name="senderColor1" format="color" /> + <attr name="senderColor2" format="color" /> + <attr name="senderColor3" format="color" /> + <attr name="senderColor4" format="color" /> + <attr name="senderColor5" format="color" /> + <attr name="senderColor6" format="color" /> + <attr name="senderColor7" format="color" /> + <attr name="senderColor8" format="color" /> + <attr name="senderColor9" format="color" /> + <attr name="senderColorA" format="color" /> + <attr name="senderColorB" format="color" /> + <attr name="senderColorC" format="color" /> + <attr name="senderColorD" format="color" /> + <attr name="senderColorE" format="color" /> + <attr name="senderColorF" format="color" /> + + <!-- mirc colors --> + + <attr name="mircColor0" format="color" /> + <attr name="mircColor1" format="color" /> + <attr name="mircColor2" format="color" /> + <attr name="mircColor3" format="color" /> + <attr name="mircColor4" format="color" /> + <attr name="mircColor5" format="color" /> + <attr name="mircColor6" format="color" /> + <attr name="mircColor7" format="color" /> + <attr name="mircColor8" format="color" /> + <attr name="mircColor9" format="color" /> + <attr name="mircColorA" format="color" /> + <attr name="mircColorB" format="color" /> + <attr name="mircColorC" format="color" /> + <attr name="mircColorD" format="color" /> + <attr name="mircColorE" format="color" /> + <attr name="mircColorF" format="color" /> + + <!-- Background and foreground colors for UI --> + + <attr name="colorForeground" format="color" /> + <attr name="colorForegroundHighlight" format="color" /> + <attr name="colorForegroundSecondary" format="color" /> + + <attr name="colorBackground" format="color" /> + <attr name="colorBackgroundHighlight" format="color" /> + <attr name="colorBackgroundCard" format="color" /> + + <!-- Tint colors for drawer --> + + <attr name="colorTintActivity" format="color" /> + <attr name="colorTintMessage" format="color" /> + <attr name="colorTintHighlight" format="color" /> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d999eaf81..dfce6f5d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,4 +2,31 @@ <string name="app_name">QuasselDroid</string> <string name="action_settings">Settings</string> <string name="default_port">4242</string> + + <string name="username_hostmask">%1$s (%2$s)</string> + + <!-- Message format strings --> + <string name="message_plain">%1$s: %2$s</string> + + <string name="message_join">%1$s has joined %2$s</string> + + <string name="message_part">%1$s has left %2$s</string> + <string name="message_part_extra">"%1$s has left %2$s (%3$s)</string> + + <string name="message_quit">%1$s has quit</string> + <string name="message_quit_extra">%1$s has quit (%2$s)</string> + + <string name="message_kill">%1$s was killed: %2$s</string> + + <string name="message_kick">%1$s has kicked %2$s from %3$s</string> + <string name="message_kick_extra">%1$s has kicked %2$s from %3$s: %4$s</string> + + <string name="message_mode">Mode %1$s by %2$s</string> + + <string name="message_nick_self">You are now known as %1$s</string> + <string name="message_nick_other">%1$s is now known as %2$s</string> + + <string name="message_daychange">{ Day changed to %1$s }</string> + + <string name="message_action">* %1$s %2$s</string> </resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 47854a96c..d0b683875 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,25 +1,23 @@ <resources> <!-- Base application theme. --> - <style name="AppTheme" parent="Theme.AppCompat.NoActionBar"> + <style name="AppTheme" parent="MaterialDrawerTheme"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> - <item name="iconServer">@drawable/ic_server_light</item> <item name="dividerColor">@color/material_drawer_dark_divider</item> <item name="messagesBackground">@color/messagesBackgroundDark</item> <item name="chatlineBackground">@color/md_dark_cards</item> </style> - <style name="AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar"> + <style name="AppTheme.Light" parent="MaterialDrawerTheme.Light"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> - <item name="iconServer">@drawable/ic_server_dark</item> <item name="dividerColor">@color/material_drawer_divider</item> <item name="messagesBackground">@color/messagesBackgroundLight</item> <item name="chatlineBackground">@color/md_light_cards</item> @@ -28,5 +26,4 @@ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Dark" /> - </resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..af5d1ca06 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="Material_Dark" parent="AppTheme"> + <item name="senderColor0">@color/md_pink_500</item> + <item name="senderColor1">@color/md_purple_500</item> + <item name="senderColor2">@color/md_red_500</item> + <item name="senderColor3">@color/md_green_500</item> + <item name="senderColor4">@color/md_cyan_500</item> + <item name="senderColor5">@color/md_deep_purple_500</item> + <item name="senderColor6">@color/md_amber_500</item> + <item name="senderColor7">@color/md_blue_500</item> + <item name="senderColor8">@color/md_pink_500</item> + <item name="senderColor9">@color/md_purple_500</item> + <item name="senderColorA">@color/md_red_500</item> + <item name="senderColorB">@color/md_green_500</item> + <item name="senderColorC">@color/md_cyan_500</item> + <item name="senderColorD">@color/md_deep_purple_500</item> + <item name="senderColorE">@color/md_amber_500</item> + <item name="senderColorF">@color/md_blue_500</item> + + <item name="colorForeground">@color/md_dark_primary_text</item> + <item name="colorForegroundHighlight">@color/md_dark_primary_text</item> + <item name="colorForegroundSecondary">@color/md_dark_secondary</item> + + <item name="colorBackground">@color/md_dark_background</item> + <item name="colorBackgroundHighlight">#ff8811</item> + <item name="colorBackgroundCard">@color/md_dark_cards</item> + + <item name="colorTintActivity">#88cc33</item> + <item name="colorTintMessage">#2277dd</item> + <item name="colorTintHighlight">#ff8811</item> + </style> + + <style name="Material_Light" parent="AppTheme"> + <item name="senderColor0">@color/md_pink_500</item> + <item name="senderColor1">@color/md_purple_500</item> + <item name="senderColor2">@color/md_red_500</item> + <item name="senderColor3">@color/md_green_500</item> + <item name="senderColor4">@color/md_cyan_500</item> + <item name="senderColor5">@color/md_deep_purple_500</item> + <item name="senderColor6">@color/md_amber_500</item> + <item name="senderColor7">@color/md_blue_500</item> + <item name="senderColor8">@color/md_pink_500</item> + <item name="senderColor9">@color/md_purple_500</item> + <item name="senderColorA">@color/md_red_500</item> + <item name="senderColorB">@color/md_green_500</item> + <item name="senderColorC">@color/md_cyan_500</item> + <item name="senderColorD">@color/md_deep_purple_500</item> + <item name="senderColorE">@color/md_amber_500</item> + <item name="senderColorF">@color/md_blue_500</item> + + <item name="colorForeground">@color/md_light_primary_text</item> + <item name="colorForegroundHighlight">@color/md_light_primary_text</item> + <item name="colorForegroundSecondary">@color/md_light_secondary</item> + + <item name="colorBackground">@color/md_light_background</item> + <item name="colorBackgroundHighlight">#ff8811</item> + <item name="colorBackgroundCard">@color/md_light_cards</item> + + <item name="colorTintActivity">#88cc33</item> + <item name="colorTintMessage">#2277dd</item> + <item name="colorTintHighlight">#ff8811</item> + </style> + + <style name="Quassel" parent="AppTheme.Light"> + <item name="senderColor0">#e90d7f</item> + <item name="senderColor1">#8e55e9</item> + <item name="senderColor2">#b30e0e</item> + <item name="senderColor3">#17b339</item> + <item name="senderColor4">#58afb3</item> + <item name="senderColor5">#9d54b3</item> + <item name="senderColor6">#b39775</item> + <item name="senderColor7">#3176b3</item> + <item name="senderColor8">#e90d7f</item> + <item name="senderColor9">#8e55e9</item> + <item name="senderColorA">#b30e0e</item> + <item name="senderColorB">#17b339</item> + <item name="senderColorC">#58afb3</item> + <item name="senderColorD">#9d54b3</item> + <item name="senderColorE">#b39775</item> + <item name="senderColorF">#3176b3</item> + + <item name="colorForeground">@color/md_light_primary_text</item> + <item name="colorForegroundHighlight">@color/md_light_primary_text</item> + <item name="colorForegroundSecondary">@color/md_light_secondary</item> + + <item name="colorBackground">@color/md_light_background</item> + <item name="colorBackgroundHighlight">#ff8811</item> + <item name="colorBackgroundCard">@color/md_light_cards</item> + + <item name="colorTintActivity">#88cc33</item> + <item name="colorTintMessage">#2277dd</item> + <item name="colorTintHighlight">#ff8811</item> + </style> +</resources> \ No newline at end of file diff --git a/build.gradle b/build.gradle index fe48ba81f..26a7c8629 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-alpha2' + classpath 'com.android.tools.build:gradle:2.0.0-alpha3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files -- GitLab