package eu.hgross.blaubot.android.views;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import eu.hgross.blaubot.android.R;
import eu.hgross.blaubot.core.Blaubot;
import eu.hgross.blaubot.messaging.BlaubotChannelManager;
import eu.hgross.blaubot.messaging.BlaubotChannelManagerInfo;
import eu.hgross.blaubot.messaging.ChannelInfo;
import eu.hgross.blaubot.ui.IBlaubotDebugView;
/**
* Android view to display informations about the StateMachine's history of states.
* Add this view to a blaubot instance like this: stateView.registerBlaubotInstance(blaubot);
*
* @author Henning Gross {@literal (mail.to@henning-gross.de)}
*/
public class ChannelManagerView extends LinearLayout implements IBlaubotDebugView {
private static final String LOG_TAG = "ChannelManagerView";
private Handler mUiHandler;
private Blaubot mBlaubot;
public static final long UPDATE_INTERVAL = 1000;
private ScheduledExecutorService scheduledThreadPoolExecutor;
private boolean initialized = false;
/**
* Maps the channel id to the corresponding view.
*/
private ConcurrentHashMap<Short, ChannelView> mChannelViews;
/**
* Shows the number of queued messages in all message senders
*/
private TextView mQueuedMessagesTextView;
/**
* Shows the number of queued bytes in all message senders
*/
private TextView mQueuedBytesTextView;
private TextView mNumberOfBytesSent;
private TextView mNumberOfMessagesSent;
public ChannelManagerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ChannelManagerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
/**
* Hacky way to update the state of the ServerConnector.
*/
private Runnable updateUiTask = new Runnable() {
@Override
public void run() {
final BlaubotChannelManagerInfo info;
if (initialized && mBlaubot != null) {
final BlaubotChannelManager channelManager = mBlaubot.getChannelManager();
info = channelManager.createChannelManagerInfo();
} else {
info = null;
}
mUiHandler.post(new Runnable() {
@Override
public void run() {
if (info != null) {
updateUI(info);
}
}
});
}
};
private void initView() {
this.mUiHandler = new Handler(Looper.getMainLooper());
this.mChannelViews = new ConcurrentHashMap<>();
this.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
// start updates
scheduledThreadPoolExecutor = Executors.newSingleThreadScheduledExecutor();
scheduledThreadPoolExecutor.scheduleAtFixedRate(updateUiTask, (long) 0, UPDATE_INTERVAL, TimeUnit.MILLISECONDS);
}
@Override
public void onViewDetachedFromWindow(View v) {
// stop updates
if (scheduledThreadPoolExecutor != null) {
scheduledThreadPoolExecutor.shutdown();
scheduledThreadPoolExecutor = null;
}
}
});
View v = createStatusTextView();
// addView(v);
this.initialized = true;
}
/**
* Creates the status text view showing the MessageSender's queue informations
*
* @return the view
*/
private View createStatusTextView() {
View v = inflate(getContext(), R.layout.blaubot_channelmanager_view, this);
mQueuedMessagesTextView = (TextView) v.findViewById(R.id.numberOfQueuedMessages);
mQueuedBytesTextView = (TextView) v.findViewById(R.id.numberOfQueuedBytes);
mNumberOfBytesSent = (TextView) v.findViewById(R.id.numberOfBytesSent);
mNumberOfMessagesSent = (TextView) v.findViewById(R.id.numberOfMessagesSent);
mQueuedMessagesTextView.setText("0");
mQueuedBytesTextView.setText("0");
mNumberOfBytesSent.setText("0");
mNumberOfMessagesSent.setText("0");
return v;
}
/**
* Updates all ChannelViews
*
* @param channelManagerInfo the channel manager info to update them from
*/
private void updateUI(final BlaubotChannelManagerInfo channelManagerInfo) {
final long numberOfQueuedMessageSenderBytes = channelManagerInfo.getNumberOfQueuedMessageSenderBytes();
final long numberOfQueuedMessageSenderMessages = channelManagerInfo.getNumberOfQueuedMessageSenderMessages();
final long numberOfBytesSent = channelManagerInfo.getNumberOfBytesSent();
final long numberOfMessagesSent = channelManagerInfo.getNumberOfMessagesSent();
// append the views
mUiHandler.post(new Runnable() {
@Override
public void run() {
mQueuedBytesTextView.setText(ViewUtils.humanReadableByteCount(numberOfQueuedMessageSenderBytes , false) + "");
mQueuedMessagesTextView.setText(numberOfQueuedMessageSenderMessages + "");
mNumberOfBytesSent.setText(ViewUtils.humanReadableByteCount(numberOfBytesSent, false) + "");
mNumberOfMessagesSent.setText(numberOfMessagesSent + "");
Set<Short> channels = new HashSet<Short>();
for (ChannelInfo channelInfo : channelManagerInfo.getChannels()) {
final short channelId = channelInfo.getChannelConfig().getChannelId();
channels.add(channelId);
ChannelView channelView = new ChannelView(getContext(), null);
boolean added = mChannelViews.putIfAbsent(channelId, channelView) == null;
channelView = mChannelViews.get(channelId);
if (added) {
addView(channelView);
}
channelView.setChannelInfo(channelInfo);
}
// remove channels that are not in the list anymore
for (Map.Entry<Short, ChannelView> entry : mChannelViews.entrySet()) {
if (!channels.contains(entry.getKey())) {
removeView(entry.getValue());
}
}
invalidate();
}
});
}
/**
* Register this view with the given blaubot instance
*
* @param blaubot the blaubot instance to connect with
*/
public void registerBlaubotInstance(Blaubot blaubot) {
if (mBlaubot != null) {
unregisterBlaubotInstance();
}
this.mBlaubot = blaubot;
}
@Override
public void unregisterBlaubotInstance() {
if (mBlaubot != null) {
// unregister listeners
this.mBlaubot = null;
}
}
}