package org.droidplanner.android.fragments.calibration.imu;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.o3dr.android.client.Drone;
import com.o3dr.android.client.apis.CalibrationApi;
import com.o3dr.services.android.lib.drone.attribute.AttributeEvent;
import com.o3dr.services.android.lib.drone.attribute.AttributeEventExtra;
import com.o3dr.services.android.lib.drone.attribute.AttributeType;
import com.o3dr.services.android.lib.drone.property.State;
import com.o3dr.services.android.lib.model.SimpleCommandListener;
import org.droidplanner.android.R;
import org.droidplanner.android.fragments.helpers.ApiListenerFragment;
import org.droidplanner.android.notifications.TTSNotificationProvider;
public class FragmentSetupIMU extends ApiListenerFragment {
private final static long TIMEOUT_MAX = 30000l; //ms
private final static long UPDATE_TIMEOUT_PERIOD = 100l; //ms
private static final String EXTRA_UPDATE_TIMESTAMP = "extra_update_timestamp";
private static final IntentFilter intentFilter = new IntentFilter();
static {
intentFilter.addAction(AttributeEvent.CALIBRATION_IMU);
intentFilter.addAction(AttributeEvent.CALIBRATION_IMU_TIMEOUT);
intentFilter.addAction(AttributeEvent.STATE_CONNECTED);
intentFilter.addAction(AttributeEvent.STATE_DISCONNECTED);
}
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
switch (action) {
case AttributeEvent.CALIBRATION_IMU: {
String message = intent.getStringExtra(AttributeEventExtra.EXTRA_CALIBRATION_IMU_MESSAGE);
if (message != null)
processMAVMessage(message, true);
break;
}
case AttributeEvent.STATE_CONNECTED:
if (calibration_step == 0) {
//Reset the screen, and enable the calibration button
resetCalibration();
btnStep.setEnabled(true);
}
break;
case AttributeEvent.STATE_DISCONNECTED:
//Reset the screen, and disable the calibration button
btnStep.setEnabled(false);
resetCalibration();
break;
case AttributeEvent.CALIBRATION_IMU_TIMEOUT:
if (getDrone().isConnected()) {
String message = intent.getStringExtra(AttributeEventExtra.EXTRA_CALIBRATION_IMU_MESSAGE);
if (message != null)
relayInstructions(message);
}
break;
}
}
};
private long updateTimestamp;
private int calibration_step = 0;
private TextView textViewStep;
private TextView textViewOffset;
private TextView textViewScaling;
private TextView textViewTimeOut;
private ProgressBar pbTimeOut;
private String timeLeftStr;
private Drawable drawableGood, drawableWarning, drawablePoor;
private final Handler handler = new Handler();
private Button btnStep;
private TextView textDesc;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_setup_imu_main, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
textViewStep = (TextView) view.findViewById(R.id.textViewIMUStep);
textViewOffset = (TextView) view.findViewById(R.id.TextViewIMUOffset);
textViewScaling = (TextView) view.findViewById(R.id.TextViewIMUScaling);
textViewTimeOut = (TextView) view.findViewById(R.id.textViewIMUTimeOut);
pbTimeOut = (ProgressBar) view.findViewById(R.id.progressBarTimeOut);
textDesc = (TextView) view.findViewById(R.id.textViewDesc);
btnStep = (Button) view.findViewById(R.id.buttonStep);
btnStep.setEnabled(false);
btnStep.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
processCalibrationStep(calibration_step);
}
});
pbTimeOut.setVisibility(View.INVISIBLE);
textViewTimeOut.setVisibility(View.INVISIBLE);
textViewOffset.setVisibility(View.INVISIBLE);
textViewScaling.setVisibility(View.INVISIBLE);
timeLeftStr = getString(R.string.setup_imu_timeleft);
drawableGood = getResources().getDrawable(R.drawable.pstate_good);
drawableWarning = getResources().getDrawable(R.drawable.pstate_warning);
drawablePoor = getResources().getDrawable(R.drawable.pstate_poor);
if(savedInstanceState != null){
updateTimestamp = savedInstanceState.getLong(EXTRA_UPDATE_TIMESTAMP);
}
}
@Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_UPDATE_TIMESTAMP, updateTimestamp);
}
private void resetCalibration(){
calibration_step = 0;
updateDescription(calibration_step);
}
@Override
public void onApiConnected() {
Drone drone = getDrone();
State droneState = drone.getAttribute(AttributeType.STATE);
if (drone.isConnected() && !droneState.isFlying()) {
btnStep.setEnabled(true);
if (droneState.isCalibrating()) {
processMAVMessage(droneState.getCalibrationStatus(), false);
}
else{
resetCalibration();
}
} else {
btnStep.setEnabled(false);
resetCalibration();
}
getBroadcastManager().registerReceiver(broadcastReceiver, intentFilter);
}
@Override
public void onApiDisconnected() {
getBroadcastManager().unregisterReceiver(broadcastReceiver);
}
private void processCalibrationStep(int step) {
if (step == 0) {
startCalibration();
updateTimestamp = System.currentTimeMillis();
} else if (step > 0 && step < 7) {
sendAck(step);
} else {
calibration_step = 0;
textViewStep.setText(R.string.setup_imu_step);
textViewOffset.setVisibility(View.INVISIBLE);
textViewScaling.setVisibility(View.INVISIBLE);
updateDescription(calibration_step);
}
}
public void updateDescription(int calibration_step) {
int id;
switch (calibration_step) {
case 0:
id = R.string.setup_imu_start;
break;
case 1:
id = R.string.setup_imu_normal;
break;
case 2:
id = R.string.setup_imu_left;
break;
case 3:
id = R.string.setup_imu_right;
break;
case 4:
id = R.string.setup_imu_nosedown;
break;
case 5:
id = R.string.setup_imu_noseup;
break;
case 6:
id = R.string.setup_imu_back;
break;
case 7:
id = R.string.setup_imu_completed;
break;
default:
return;
}
if (textDesc != null) {
textDesc.setText(id);
}
if (btnStep != null) {
if (calibration_step == 0)
btnStep.setText(R.string.button_setup_calibrate);
else if (calibration_step == 7)
btnStep.setText(R.string.button_setup_done);
else
btnStep.setText(R.string.button_setup_next);
}
if (calibration_step == 7 || calibration_step == 0) {
handler.removeCallbacks(runnable);
pbTimeOut.setVisibility(View.INVISIBLE);
textViewTimeOut.setVisibility(View.INVISIBLE);
} else {
handler.removeCallbacks(runnable);
textViewTimeOut.setVisibility(View.VISIBLE);
pbTimeOut.setIndeterminate(true);
pbTimeOut.setVisibility(View.VISIBLE);
handler.postDelayed(runnable, UPDATE_TIMEOUT_PERIOD);
}
}
private void sendAck(int step) {
Drone dpApi = getDrone();
if (dpApi.isConnected()) {
CalibrationApi.getApi(dpApi).sendIMUAck(step);
}
}
private void startCalibration() {
Drone dpApi = getDrone();
if (dpApi.isConnected()) {
CalibrationApi.getApi(dpApi).startIMUCalibration(new SimpleCommandListener(){
@Override
public void onError(int error){
Toast.makeText(getActivity(), R.string.imu_calibration_start_error, Toast.LENGTH_LONG).show();
}
});
}
}
private void processMAVMessage(String message, boolean updateTime) {
if (message.contains("Place") || message.contains("Calibration")) {
if(updateTime) {
updateTimestamp = System.currentTimeMillis();
}
processOrientation(message);
}
else if (message.contains("Offsets")) {
textViewOffset.setVisibility(View.VISIBLE);
textViewOffset.setText(message);
} else if (message.contains("Scaling")) {
textViewScaling.setVisibility(View.VISIBLE);
textViewScaling.setText(message);
}
}
private void processOrientation(String message) {
if (message.contains("level"))
calibration_step = 1;
else if (message.contains("LEFT"))
calibration_step = 2;
else if (message.contains("RIGHT"))
calibration_step = 3;
else if (message.contains("DOWN"))
calibration_step = 4;
else if (message.contains("UP"))
calibration_step = 5;
else if (message.contains("BACK"))
calibration_step = 6;
else if (message.contains("Calibration"))
calibration_step = 7;
String msg = message.replace("any key.", "'Next'");
relayInstructions(msg);
textViewStep.setText(msg);
updateDescription(calibration_step);
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
handler.removeCallbacks(this);
updateTimeOutProgress();
handler.postDelayed(this, UPDATE_TIMEOUT_PERIOD);
}
};
private void relayInstructions(String instructions){
final Activity activity = getActivity();
if(activity == null) return;
final Context context = activity.getApplicationContext();
getBroadcastManager()
.sendBroadcast(new Intent(TTSNotificationProvider.ACTION_SPEAK_MESSAGE)
.putExtra(TTSNotificationProvider.EXTRA_MESSAGE_TO_SPEAK, instructions));
Toast.makeText(context, instructions, Toast.LENGTH_LONG).show();
}
protected void updateTimeOutProgress() {
final long timeElapsed = System.currentTimeMillis() - updateTimestamp;
long timeLeft = (int) (TIMEOUT_MAX - timeElapsed);
if (timeLeft >= 0) {
int secLeft = (int) (timeLeft / 1000) + 1;
pbTimeOut.setIndeterminate(false);
pbTimeOut.setMax((int) TIMEOUT_MAX);
pbTimeOut.setProgress((int) timeLeft);
textViewTimeOut.setText(timeLeftStr + String.valueOf(secLeft) + "s");
if (secLeft > 15)
pbTimeOut.setProgressDrawable(drawableGood);
else if (secLeft <= 15 && secLeft > 5)
pbTimeOut.setProgressDrawable(drawableWarning);
else if (secLeft == 5)
pbTimeOut.setProgressDrawable(drawablePoor);
} else {
textViewTimeOut.setText(timeLeftStr + "0s");
}
}
}