Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: fix: state lost on orientation change in sensor activities #2576

Draft
wants to merge 15 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 285 additions & 0 deletions app/src/main/java/io/pslab/sensors/AbstractSensorActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package io.pslab.sensors;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.text.Editable;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import java.util.Locale;

import io.pslab.R;
import io.pslab.communication.ScienceLab;
import io.pslab.others.ScienceLabCommon;

abstract class AbstractSensorActivity extends AppCompatActivity {
private static final String TAG = AbstractSensorActivity.class.getSimpleName();

private static final String KEY_PLAY = TAG + "_play";
private static final String KEY_RUN_INDEFINITELY = TAG + "_runIndefinitely";
private static final String KEY_TIMEGAP = TAG + "_timegap";
private static final String KEY_FLAG = TAG + "_flag";
private static final String KEY_STARTTIME = TAG + "_starttime";
private static final String KEY_COUNTER = TAG + "_counter";

private int counter;
private ScienceLab scienceLab;
private long startTime;
private int flag;
private boolean play = false;
private boolean runIndefinitely = true;
private int timeGap = 100;

private RelativeLayout sensorDock;
private SeekBar timeGapSeekbar;
private TextView timeGapLabel;
private CheckBox indefiniteSamplesCheckBox;
private EditText samplesEditBox;
private ImageButton playPauseButton;

private HandlerThread handlerThread;
private Handler handler;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());

if (savedInstanceState != null) {
play = savedInstanceState.getBoolean(KEY_PLAY);
runIndefinitely = savedInstanceState.getBoolean(KEY_RUN_INDEFINITELY);
timeGap = savedInstanceState.getInt(KEY_TIMEGAP);
flag = savedInstanceState.getInt(KEY_FLAG);
startTime = savedInstanceState.getLong(KEY_STARTTIME);
counter = savedInstanceState.getInt(KEY_COUNTER);
}

sensorDock = findViewById(R.id.sensor_control_dock_layout);
indefiniteSamplesCheckBox = sensorDock.findViewById(R.id.checkBox_samples_sensor);
timeGapSeekbar = sensorDock.findViewById(R.id.seekBar_timegap_sensor);
timeGapLabel = sensorDock.findViewById(R.id.tv_timegap_label);
samplesEditBox = sensorDock.findViewById(R.id.editBox_samples_sensors);
playPauseButton = sensorDock.findViewById(R.id.imageButton_play_pause_sensor);

Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(getTitleResId());
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}

setSensorDock();
sensorDock.setVisibility(View.VISIBLE);

scienceLab = ScienceLabCommon.scienceLab;

handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
handler = new Handler(looper);

if (play) {
startTimerTask();
}
}

@Override
protected void onDestroy() {
play = false;
handlerThread.quit();
super.onDestroy();
}

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);

outState.putBoolean(KEY_PLAY, play);
outState.putBoolean(KEY_RUN_INDEFINITELY, runIndefinitely);
outState.putInt(KEY_TIMEGAP, timeGap);
outState.putInt(KEY_FLAG, flag);
outState.putLong(KEY_STARTTIME, startTime);
}

@NonNull
private Runnable getTimerTask() {

return () -> {
if (play && scienceLab.isConnected() && shouldPlay()) {
if (flag == 0) {
startTime = System.currentTimeMillis();
flag = 1;
}
SensorDataFetch sensorDataFetch = getSensorDataFetch();
if (sensorDataFetch == null) {
Log.w(TAG, "No SensorDataFetch!");
} else {
sensorDataFetch.execute();
}
if (play) {
startTimerTask();
}
} else {
setPlayButton(false);
}
};
}

private void startTimerTask() {
handler.postDelayed(getTimerTask(), timeGap);
}

protected ScienceLab getScienceLab() {
return scienceLab;
}

private void setSensorDock() {
final int step = 1;
final int max = 1000;
final int min = 100;

playPauseButton.setOnClickListener(v -> {
if (play && scienceLab.isConnected()) {
play = false;
} else if (!scienceLab.isConnected()) {
play = false;
} else {
play = true;
startTimerTask();
if (!indefiniteSamplesCheckBox.isChecked()) {
Editable text = samplesEditBox.getText();
counter = text.length() == 0 ? 0 : Integer.parseInt(text.toString());
}
}
setPlayButton(play);
});

setPlayButton(play);

sensorDock.setVisibility(View.VISIBLE);

indefiniteSamplesCheckBox.setChecked(runIndefinitely);
samplesEditBox.setEnabled(!runIndefinitely);
samplesEditBox.setText(String.valueOf(counter));
indefiniteSamplesCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
runIndefinitely = true;
samplesEditBox.setEnabled(false);
} else {
runIndefinitely = false;
samplesEditBox.setEnabled(true);
}
});

timeGapSeekbar.setMax((max - min) / step);
timeGapSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
timeGap = min + (progress * step);
timeGapLabel.setText(
String.format(
Locale.getDefault(),
"%d%s", timeGap, getString(R.string.unit_milliseconds)
)
);
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// nothing to do here
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// nothing to do here
}
});
}

private void setPlayButton(boolean isPlaying) {
playPauseButton.setImageResource(isPlaying ? R.drawable.circle_pause_button : R.drawable.circle_play_button);
}

protected boolean shouldPlay() {
if (play) {
if (indefiniteSamplesCheckBox.isChecked())
return true;
else if (counter > 0) {
counter--;
return true;
} else {
play = false;
return false;
}
} else {
return false;
}
}

/**
* Get time of first start of sensor data capture.
*
* @return time in ms
*/
protected long getStartTime() {
return startTime;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return true;
}

protected abstract SensorDataFetch getSensorDataFetch();

protected abstract int getLayoutResId();

protected abstract int getTitleResId();

protected abstract class SensorDataFetch {

private boolean isSensorDataAcquired;

protected float getTimeElapsed() {
return (System.currentTimeMillis() - getStartTime()) / 1000f;
}

protected boolean isSensorDataAcquired() {
return isSensorDataAcquired;
}

protected void execute() {
getSensorData();
isSensorDataAcquired = true;
updateUi();
}

abstract void getSensorData();

abstract void updateUi();

}


}
Loading
Loading