package eu.hgross.blaubot.android.views; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; import android.os.Handler; import android.os.Looper; import android.os.Vibrator; import android.util.AttributeSet; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; import com.google.gson.Gson; import java.util.Date; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import eu.hgross.blaubot.android.R; import eu.hgross.blaubot.core.Blaubot; import eu.hgross.blaubot.core.BlaubotConstants; import eu.hgross.blaubot.core.IBlaubotDevice; import eu.hgross.blaubot.core.ILifecycleListener; import eu.hgross.blaubot.messaging.BlaubotMessage; import eu.hgross.blaubot.messaging.IBlaubotChannel; import eu.hgross.blaubot.messaging.IBlaubotMessageListener; import eu.hgross.blaubot.ui.BlaubotDebugViewConstants; import eu.hgross.blaubot.ui.IBlaubotDebugView; import eu.hgross.blaubot.ui.PingMessage; import eu.hgross.blaubot.util.Log; import eu.hgross.blaubot.util.PingMeasurer; import eu.hgross.blaubot.util.PingMeasurerResult; /** * Android view to send and receive pings on a blaubot channel * * Add this view to a blaubot instance like this: pingView.registerBlaubotInstance(blaubot); * * @author Henning Gross {@literal (mail.to@henning-gross.de)} */ public class PingView extends FrameLayout implements IBlaubotDebugView { private static final short CHANNEL_ID = BlaubotDebugViewConstants.PING_VIEW_CHANNEL_ID; private static final int VIBRATE_TIME = 350; /** * Number of Ping messages to be sent by the measurement when a long click on the ping button occurs */ public static final int NUMBER_OF_PINGS_FOR_MEASURE = 50; private static final String LOG_TAG = "PingView"; private TextView mLastReceivedPingTextView; private Button mPingButton; private Handler mUiHandler; private Blaubot mBlaubot; private IBlaubotChannel mChannel; private Vibrator mVibrator; private boolean mUseVibrator; private Gson mGson; public PingView(Context context, AttributeSet attrs) { super(context, attrs); initView(context, attrs); } public PingView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context, attrs); } private void initView(Context context, AttributeSet attrs) { mGson = new Gson(); View view = inflate(context, R.layout.blaubot_ping_view, null); mUiHandler = new Handler(Looper.getMainLooper()); if (attrs != null) { TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.PingView, 0, 0); try { mUseVibrator = a.getBoolean(R.styleable.PingView_vibrateOnPing, mUseVibrator); } finally { a.recycle(); } } addView(view); mLastReceivedPingTextView = (TextView) findViewById(R.id.lastReceivedPingTimestamp); mPingButton = (Button) findViewById(R.id.pingButton); mPingButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mChannel != null) { PingMessage pingMessage = new PingMessage(); pingMessage.setSenderUniqueDeviceId(mBlaubot.getOwnDevice().getUniqueDeviceID()); pingMessage.setTimestamp(System.currentTimeMillis()); String serialized = mGson.toJson(pingMessage); final boolean published = mChannel.publish(serialized.getBytes(BlaubotConstants.STRING_CHARSET)); if (!published) { Toast.makeText(getContext(), "Ping was not sent: Queue is full", Toast.LENGTH_LONG).show(); } } } }); mPingButton.setLongClickable(true); mPingButton.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mChannel != null && mBlaubot != null) { new Thread(new Runnable() { @Override public void run() { PingMeasurer measurer = new PingMeasurer(mChannel, mBlaubot.getOwnDevice()); final Future<PingMeasurerResult> measure = measurer.measure(NUMBER_OF_PINGS_FOR_MEASURE); try { final PingMeasurerResult pingMeasurerResult = measure.get(); if (Log.logDebugMessages()) { Log.d(LOG_TAG, "PingMeasurement completed: " + pingMeasurerResult); } mUiHandler.post(new Runnable() { @Override public void run() { // custom view PingMeasureResultView pingMeasureResultView = new PingMeasureResultView(getContext()); pingMeasureResultView.setPingMeasureResult(pingMeasurerResult); // show dialog AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setView(pingMeasureResultView); builder.setTitle("Ping measurement result"); builder.setPositiveButton("close", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); final AlertDialog alertDialog = builder.create(); alertDialog.show(); } }); } catch (InterruptedException e) { } catch (ExecutionException e) { } } }).start(); } else { Toast.makeText(getContext(), "Error: No Blaubot instance registered!", Toast.LENGTH_SHORT).show(); } return true; } }); } private IBlaubotMessageListener mMessageListener = new IBlaubotMessageListener() { @Override public void onMessage(BlaubotMessage blaubotMessage) { final Date receivedDate = new Date(); final String msg = new String(blaubotMessage.getPayload(), BlaubotConstants.STRING_CHARSET); final PingMessage pingMessage = mGson.fromJson(msg, PingMessage.class); mUiHandler.post(new Runnable() { @Override public void run() { Toast.makeText(getContext(), pingMessage.toString(), Toast.LENGTH_SHORT).show(); StringBuilder sb = new StringBuilder(receivedDate.toString()); if (pingMessage.getSenderUniqueDeviceId().equals(mBlaubot.getOwnDevice().getUniqueDeviceID())) { // append RTT long rtt = receivedDate.getTime() - pingMessage.getTimestamp(); sb.append("\n"); sb.append("RTT: "); sb.append(rtt); sb.append(" ms"); } mLastReceivedPingTextView.setText(sb.toString()); } }); if (mVibrator != null && mUseVibrator) { mVibrator.vibrate(VIBRATE_TIME); } } }; /** * 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; this.mChannel = this.mBlaubot.createChannel(CHANNEL_ID); this.mChannel.getChannelConfig().setTransmitReflexiveMessages(true); // needed to measure a meaningful rtt this.mChannel.subscribe(mMessageListener); // this.mBlaubot.addLifecycleListener(new ILifecycleListener() { // @Override // public void onConnected() { // final PingMessage src = new PingMessage(); // src.setTimestamp(System.currentTimeMillis()); // src.setSenderUniqueDeviceId(mBlaubot.getOwnDevice().getUniqueDeviceID()); // mChannel.publish(mGson.toJson(src).getBytes()); // } // // @Override // public void onDisconnected() { // // } // // @Override // public void onDeviceJoined(IBlaubotDevice blaubotDevice) { // // } // // @Override // public void onDeviceLeft(IBlaubotDevice blaubotDevice) { // // } // // @Override // public void onPrinceDeviceChanged(IBlaubotDevice oldPrince, IBlaubotDevice newPrince) { // // } // // @Override // public void onKingDeviceChanged(IBlaubotDevice oldKing, IBlaubotDevice newKing) { // // } // }); if (mUseVibrator) { mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); } } @Override public void unregisterBlaubotInstance() { if (mBlaubot != null) { this.mChannel.unsubscribe(); this.mChannel.removeMessageListener(mMessageListener); this.mBlaubot = null; } } }