package eu.hgross.blaubot.android.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.apache.commons.collections4.queue.CircularFifoQueue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import eu.hgross.blaubot.android.R;
import eu.hgross.blaubot.core.Blaubot;
import eu.hgross.blaubot.core.State;
import eu.hgross.blaubot.core.acceptor.ConnectionMetaDataDTO;
import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotDiscoveryEventListener;
import eu.hgross.blaubot.core.statemachine.events.AbstractBlaubotDeviceDiscoveryEvent;
import eu.hgross.blaubot.core.statemachine.events.AbstractBlaubotStateMachineEvent;
import eu.hgross.blaubot.ui.IBlaubotDebugView;
/**
* Android view to display informations about the beacons registered to the BeaconService.
*
* Add this view to a blaubot instance like this: stateView.registerBlaubotInstance(blaubot);
*
* @author Henning Gross {@literal (mail.to@henning-gross.de)}
*/
public class BeaconView extends LinearLayout implements IBlaubotDebugView {
private static final int MAX_EVENTS_DEFAULT = 5;
private Handler mUiHandler;
private Blaubot mBlaubot;
private Context mContext;
private Object queueLock = new Object();
private CircularFifoQueue<EventEntry> lastEvents;
private int mMaxEvents = MAX_EVENTS_DEFAULT;
private class EventEntry {
AbstractBlaubotStateMachineEvent event;
View view;
long time;
public EventEntry(AbstractBlaubotDeviceDiscoveryEvent discoveryEvent, View item, long time) {
this.event = discoveryEvent;
this.view = item;
this.time = time;
}
}
public BeaconView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context, attrs);
}
public BeaconView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
this.mContext = context;
if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.LifeCycleView,
0, 0);
try {
mMaxEvents = a.getInt(R.styleable.BeaconView_maxEvents, mMaxEvents);
} finally {
a.recycle();
}
}
this.lastEvents = new CircularFifoQueue<>();
mUiHandler = new Handler(Looper.getMainLooper());
}
private IBlaubotDiscoveryEventListener mDiscoveryEventListener = new IBlaubotDiscoveryEventListener() {
private void updateEntryTimes() {
final ArrayList<EventEntry> entries = new ArrayList<>(lastEvents);
Collections.reverse(entries);
synchronized (queueLock) {
long firstTime = -1;
for (EventEntry entry : entries) {
TextView whenTextView = (TextView) entry.view.findViewById(R.id.whenText);
if (firstTime == -1) {
whenTextView.setText(Html.fromHtml("<html>(latest)<br> " + (new Date(entry.time)) + "</html>"));
firstTime = entry.time;
} else {
final long delta = firstTime - entry.time;
whenTextView.setText(delta + " ms before");
}
}
}
}
@Override
public void onDeviceDiscoveryEvent(final AbstractBlaubotDeviceDiscoveryEvent discoveryEvent) {
final State state = discoveryEvent.getRemoteDeviceState();
final String uniqueDeviceID = discoveryEvent.getRemoteDevice().getUniqueDeviceID();
final Drawable icon = ViewUtils.getDrawableForBlaubotState(mContext, state);
final List<ConnectionMetaDataDTO> connectionMetaData = discoveryEvent.getConnectionMetaData();
mUiHandler.post(new Runnable() {
@Override
public void run() {
final View item = inflate(mContext, R.layout.blaubot_beacon_view_list_item, null);
TextView uniqueDeviceIdTextView = (TextView) item.findViewById(R.id.uniqueDeviceIdLabel);
TextView connectionMetaDataTextView = (TextView) item.findViewById(R.id.connectionMetaData);
TextView stateTextView = (TextView) item.findViewById(R.id.stateLabel);
ImageView iconImageView = (ImageView) item.findViewById(R.id.stateIcon);
iconImageView.setImageDrawable(icon);
uniqueDeviceIdTextView.setText(uniqueDeviceID);
stateTextView.setText(state.toString());
connectionMetaDataTextView.setText(connectionMetaData.toString());
// prepend
addView(item, 0);
// memorize
synchronized (queueLock) {
final EventEntry eventEntry = new EventEntry(discoveryEvent, item, System.currentTimeMillis());
lastEvents.add(eventEntry);
}
// remove views, if too much
final int childCount = getChildCount();
if (childCount > mMaxEvents) {
removeViews(mMaxEvents, childCount - mMaxEvents);
}
updateEntryTimes();
}
});
}
};
/**
* Register this view with the given blaubot instance
*
* @param blaubot the blaubot instance to connect with
*/
@Override
public void registerBlaubotInstance(Blaubot blaubot) {
if (mBlaubot != null) {
unregisterBlaubotInstance();
}
this.mBlaubot = blaubot;
this.mBlaubot.getConnectionStateMachine().getBeaconService().addDiscoveryEventListener(mDiscoveryEventListener);
}
@Override
public void unregisterBlaubotInstance() {
if (mBlaubot != null) {
this.mBlaubot.getConnectionStateMachine().getBeaconService().removeDiscoveryEventListener(mDiscoveryEventListener);
}
}
}