close #26 - outputstream implementiert, welche kontinuirliche daten schreibt
close #24 - können jetzt ohne probleme kommentare schreiben close #22 - startet beim frischen start aber auch beim onResume die phone app close #18 - knallt nicht mehr wenn bei 0 gestartet wird. close #9 - verbinden sich wunderbar, nachrichten fliegen und daten werden übertragen close #8 - sensordaten werden in ein .csv files geschrieben inkl kommentare
This commit is contained in:
@@ -8,8 +8,9 @@ android {
|
||||
applicationId "de.tonifetzer.conductorswatch"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
//sdk 2 | product version 3 | build num 2 | multi-apk 2
|
||||
versionCode 260120101
|
||||
versionName "0.1.2"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
||||
@@ -231,6 +231,7 @@ public class Croller extends View {
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
//called way to much!
|
||||
if (mProgressChangeListener != null)
|
||||
mProgressChangeListener.onProgressChanged((int) (deg - 2));
|
||||
|
||||
@@ -502,8 +503,10 @@ public class Croller extends View {
|
||||
}
|
||||
|
||||
public void setProgress(int x) {
|
||||
deg = x + 2;
|
||||
invalidate();
|
||||
if(deg != x + 2){
|
||||
deg = x + 2;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
@@ -511,8 +514,10 @@ public class Croller extends View {
|
||||
}
|
||||
|
||||
public void setLabel(String txt) {
|
||||
label = txt;
|
||||
invalidate();
|
||||
if(!label.equals(txt)){
|
||||
label = txt;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public int getBackCircleColor() {
|
||||
@@ -520,8 +525,10 @@ public class Croller extends View {
|
||||
}
|
||||
|
||||
public void setBackCircleColor(int backCircleColor) {
|
||||
this.backCircleColor = backCircleColor;
|
||||
invalidate();
|
||||
if(this.backCircleColor != backCircleColor){
|
||||
this.backCircleColor = backCircleColor;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMainCircleColor() {
|
||||
@@ -529,8 +536,10 @@ public class Croller extends View {
|
||||
}
|
||||
|
||||
public void setMainCircleColor(int mainCircleColor) {
|
||||
this.mainCircleColor = mainCircleColor;
|
||||
invalidate();
|
||||
if(this.mainCircleColor != mainCircleColor){
|
||||
this.mainCircleColor = mainCircleColor;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private ValueAnimator mainCircleAnimation;
|
||||
|
||||
@@ -6,50 +6,40 @@ import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Handler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerData;
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerWindowBuffer;
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.BpmEstimator;
|
||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||
import de.tonifetzer.conductorswatch.network.SensorDataFileSender;
|
||||
import de.tonifetzer.conductorswatch.network.SensorDataFileStreamer;
|
||||
import de.tonifetzer.conductorswatch.utilities.ByteStreamWriter;
|
||||
|
||||
/**
|
||||
* Created by toni on 13/11/17.
|
||||
*/
|
||||
|
||||
//TODO: einfügen der logik autoCorr + FindPeaks
|
||||
public class Estimator implements SensorEventListener {
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mAccelerometer;
|
||||
private Sensor mGyroscope;
|
||||
private Context mContext;
|
||||
private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
|
||||
private BpmEstimator mBpmEstimator;
|
||||
|
||||
private Handler mHandler;
|
||||
private int mUpdaterate_ms = 100;
|
||||
private boolean mSensorUpdateFlag = false;
|
||||
private ByteStreamWriter mByteStreamWriterAcc;
|
||||
private ByteStreamWriter mByteStreamWriterGyro;
|
||||
private SensorDataFileStreamer mStreamer;
|
||||
|
||||
|
||||
private final Runnable mProcessSensors = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if(mAccelerometerWindowBuffer.isNextWindowReady()){
|
||||
|
||||
double bpm = mBpmEstimator.estimate();
|
||||
|
||||
for (OnBpmEstimatorListener listener:listeners) {
|
||||
listener.onNewDataAvailable(bpm);
|
||||
}
|
||||
}
|
||||
|
||||
mSensorUpdateFlag = true;
|
||||
// The Runnable is posted to run again here:
|
||||
mHandler.postDelayed(this, mUpdaterate_ms);
|
||||
}
|
||||
};
|
||||
private Timer mTimer = new Timer();
|
||||
|
||||
|
||||
public Estimator(Context mContext){
|
||||
@@ -60,36 +50,73 @@ public class Estimator implements SensorEventListener {
|
||||
|
||||
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
|
||||
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
|
||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
|
||||
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(1024, 256);
|
||||
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
|
||||
|
||||
mHandler = new Handler();
|
||||
mHandler.post(mProcessSensors); //start runnable
|
||||
mTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (mAccelerometerWindowBuffer.isNextWindowReady()) {
|
||||
double bpm = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedWindow());
|
||||
|
||||
for (OnBpmEstimatorListener listener : listeners) {
|
||||
listener.onNewDataAvailable(bpm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, 100);
|
||||
|
||||
mByteStreamWriterAcc = new ByteStreamWriter();
|
||||
mByteStreamWriterGyro = new ByteStreamWriter();
|
||||
|
||||
mStreamer = new SensorDataFileStreamer(mContext);
|
||||
new Thread(mStreamer).start();
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
mHandler.removeCallbacks(mProcessSensors); //remove runnable
|
||||
|
||||
mTimer.cancel();
|
||||
mTimer.purge();
|
||||
|
||||
mSensorManager.unregisterListener(this);
|
||||
|
||||
//send data and close the ByteStreamWriter
|
||||
for (OnBpmEstimatorListener listener : listeners) {
|
||||
//listener.onEstimationStopped(mByteStreamWriter.getByteArray());
|
||||
}
|
||||
mByteStreamWriterAcc.close();
|
||||
mByteStreamWriterGyro.close();
|
||||
|
||||
mStreamer.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent se) {
|
||||
|
||||
// einkommentieren, falls die updaterate beschränkt werden soll. aktuell maximum speed
|
||||
//if(mSensorUpdateFlag) {
|
||||
//TODO: at the moment this runs in main thread... since worker fragment runs also in main thread
|
||||
|
||||
//ca 200hz, every 5 to 6 ms we have an update
|
||||
if (se.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
|
||||
mAccelerometerWindowBuffer.add(new AccelerometerData(System.currentTimeMillis(), se.values[0], se.values[1], se.values[2]));
|
||||
mByteStreamWriterAcc.writeSensor3D(Sensor.TYPE_LINEAR_ACCELERATION, se.values[0], se.values[1], se.values[2]);
|
||||
|
||||
mStreamer.sendByteArray(mByteStreamWriterAcc.getByteArray());
|
||||
mByteStreamWriterAcc.reset();
|
||||
}
|
||||
|
||||
//TODO: get also gyro and write stuff into file
|
||||
if (se.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
|
||||
//TODO: Rename AccelerometerData to SensorData3D
|
||||
mByteStreamWriterGyro.writeSensor3D(Sensor.TYPE_GYROSCOPE, se.values[0], se.values[1], se.values[2]);
|
||||
|
||||
// mSensorUpdateFlag = false;
|
||||
//}
|
||||
mStreamer.sendByteArray(mByteStreamWriterGyro.getByteArray());
|
||||
mByteStreamWriterGyro.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,12 +124,12 @@ public class Estimator implements SensorEventListener {
|
||||
// do nothin
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface for callback calculated bpm
|
||||
*/
|
||||
public interface OnBpmEstimatorListener {
|
||||
void onNewDataAvailable(double bpm);
|
||||
void onEstimationStopped(byte[] sensorData);
|
||||
}
|
||||
|
||||
private List<OnBpmEstimatorListener> listeners = new CopyOnWriteArrayList<OnBpmEstimatorListener>();
|
||||
|
||||
@@ -7,48 +7,29 @@ import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.wearable.activity.WearableActivity;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.NodeApi;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.tonifetzer.conductorswatch.network.SensorDataFileSender;
|
||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||
|
||||
public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener, TapBpm.OnTapBpmListener, GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener{
|
||||
public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener, TapBpm.OnTapBpmListener {
|
||||
|
||||
// member
|
||||
private TextView mTextView;
|
||||
private Croller mCroller;
|
||||
private GestureDetector mDetector;
|
||||
private boolean mModeRecord;
|
||||
private boolean mModeRecord = false;
|
||||
|
||||
// connection to phone stuff
|
||||
private static final String START_ACTIVITY_PATH = "/start-activity";
|
||||
private static final String START_RECORD_PATH = "/start-record";
|
||||
private static final String STOP_RECORD_PATH = "/stop-record";
|
||||
private static final String UPDATE_PATH = "/update";
|
||||
|
||||
public static final String TAG = "DataLayerListenerService";
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private Node mNode;
|
||||
private boolean mResolvingError = false;
|
||||
private SensorDataFileSender mSender;
|
||||
private volatile boolean mReadyToSend = true;
|
||||
|
||||
// display center
|
||||
private int mDisplayWidth;
|
||||
@@ -56,7 +37,8 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
private Point mDisplayCenter;
|
||||
|
||||
// saved Bpm to reset after recording
|
||||
private int mMetronomBpm;
|
||||
private int mMetronomBpm = 80;
|
||||
private int mLastSendBPM = 80;
|
||||
|
||||
// tapping
|
||||
private Thread mTapBpmThread;
|
||||
@@ -81,7 +63,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
|
||||
mModeRecord = !mModeRecord;
|
||||
|
||||
if(mModeRecord){
|
||||
if (mModeRecord) {
|
||||
WorkerFragment worker = new WorkerFragment();
|
||||
|
||||
//provide the fragment with the bpm set
|
||||
@@ -90,71 +72,71 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
worker.setArguments(args);
|
||||
|
||||
//send data to phone, that we start to record
|
||||
String curProgress = Integer.toString(mCroller.getProgress());
|
||||
sendMessage(START_RECORD_PATH + ":" + curProgress + ":" + curProgress);
|
||||
mMetronomBpm = mCroller.getProgress();
|
||||
mSender.sendMessage(START_RECORD_PATH + ":" + mMetronomBpm + ":" + mMetronomBpm);
|
||||
|
||||
//setter
|
||||
worker.setCrollerWorker(mCroller);
|
||||
worker.setTextViewWorker(mTextView);
|
||||
mMetronomBpm = mCroller.getProgress();
|
||||
|
||||
//create fragment instance
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
transaction.replace(R.id.layout, worker, "Worker");
|
||||
transaction.addToBackStack("Worker");
|
||||
transaction.commit();
|
||||
}
|
||||
else {
|
||||
getFragmentManager().popBackStack();
|
||||
mCroller.interruptMainCircleColorAnimated();
|
||||
} else {
|
||||
|
||||
mCroller.setProgressPrimaryColor(Color.parseColor("#158b69"));
|
||||
mCroller.setBackCircleColor(Color.parseColor("#158b69"));
|
||||
mTextView.setTextColor(Color.parseColor("#158b69"));
|
||||
mCroller.interruptMainCircleColorAnimated();
|
||||
mCroller.setProgress(mMetronomBpm);
|
||||
|
||||
//send data to phone, that we stopped to record
|
||||
sendMessage(STOP_RECORD_PATH);
|
||||
mSender.sendMessage(STOP_RECORD_PATH);
|
||||
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean onLongPressCustomized(MotionEvent ev){
|
||||
private boolean onLongPressCustomized(MotionEvent ev) {
|
||||
|
||||
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
|
||||
Point currentPoint = new Point((int) ev.getX(), (int) ev.getY());
|
||||
|
||||
//make sure the longPress only works within the maincircle of the scroller
|
||||
double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, (mDisplayWidth / 2.0f), (mDisplayHeight / 2.0f));
|
||||
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
|
||||
double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
|
||||
if ((distancePointToMiddle > mCroller.getBackCircleRadius())) {
|
||||
mHandler.removeCallbacks(mLongPressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_DOWN){
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mHandler.postDelayed(mLongPressed, mLongPressDelay);
|
||||
}
|
||||
|
||||
if((ev.getAction() == MotionEvent.ACTION_MOVE) || (ev.getAction() == MotionEvent.ACTION_HOVER_MOVE)){
|
||||
if ((ev.getAction() == MotionEvent.ACTION_MOVE) || (ev.getAction() == MotionEvent.ACTION_HOVER_MOVE)) {
|
||||
|
||||
if(mPreviousMovePoint == null) {
|
||||
/*
|
||||
if (mPreviousMovePoint == null) {
|
||||
mPreviousMovePoint = currentPoint;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
int dx = Math.abs(currentPoint.x - mPreviousMovePoint.x);
|
||||
int dy = Math.abs(currentPoint.y - mPreviousMovePoint.y);
|
||||
int distance = (int) Math.sqrt(dx*dx + dy*dy);
|
||||
if(distance > mDistanceJitteringLongPress) {
|
||||
int distance = (int) Math.sqrt(dx * dx + dy * dy);
|
||||
if (distance > mDistanceJitteringLongPress) {
|
||||
mHandler.removeCallbacks(mLongPressed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_UP){
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
mHandler.removeCallbacks(mLongPressed);
|
||||
if(mLongPressHandlerActivated){
|
||||
if (mLongPressHandlerActivated) {
|
||||
mLongPressHandlerActivated = false;
|
||||
mPreviousMovePoint = null;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -162,37 +144,35 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onColorChanging(MotionEvent ev, int MainColor){
|
||||
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
|
||||
private boolean onColorChanging(MotionEvent ev, int MainColor) {
|
||||
Point currentPoint = new Point((int) ev.getX(), (int) ev.getY());
|
||||
|
||||
//only works within the maincircle of the scroller
|
||||
double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
|
||||
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
|
||||
if ((distancePointToMiddle > mCroller.getBackCircleRadius())) {
|
||||
|
||||
//if we are outside the area of interest and the animation is already running, we need to reset
|
||||
if(mCroller.isMainCircleAnimationRunning() || mCroller.isBackCircleAnimationRunning()){
|
||||
if (mCroller.isMainCircleAnimationRunning() || mCroller.isBackCircleAnimationRunning()) {
|
||||
mCroller.interruptMainCircleColorAnimated();
|
||||
mCroller.interruptBackCircleAnimated();
|
||||
}
|
||||
else {
|
||||
mCroller.setBackCircleColor(Color.parseColor("#cccccc"));
|
||||
mCroller.setMainCircleColor(Color.parseColor("#ffffff"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_DOWN){
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
//push down effect back circle (ring around main circle)
|
||||
mCroller.setBackCircleColorAnimated(Color.parseColor("#cccccc"), MainColor,150);
|
||||
mCroller.setBackCircleColorAnimated(Color.parseColor("#cccccc"), MainColor, 150);
|
||||
|
||||
//make color of main circle brighter the longer we press
|
||||
mCroller.setMainCircleColorAnimated(Color.parseColor("#ffffff"), MainColor,1500);
|
||||
mCroller.setMainCircleColorAnimated(Color.parseColor("#ffffff"), MainColor, 1300);
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_UP){
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
//push_up effect back circle (ring around main circle)
|
||||
mCroller.setBackCircleColorAnimated(MainColor, Color.parseColor("#cccccc"),150);
|
||||
mCroller.setBackCircleColorAnimated(MainColor, Color.parseColor("#cccccc"), 150);
|
||||
|
||||
// if we remove our finger before longpress was recognized, reverse animation
|
||||
mCroller.stopMainCircleColorAnimated();
|
||||
@@ -201,30 +181,30 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onTapForBpm(MotionEvent ev){
|
||||
public boolean onTapForBpm(MotionEvent ev) {
|
||||
|
||||
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
|
||||
Point currentPoint = new Point((int) ev.getX(), (int) ev.getY());
|
||||
|
||||
//only works within the maincircle of the scroller
|
||||
double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
|
||||
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
|
||||
if ((distancePointToMiddle > mCroller.getBackCircleRadius())) {
|
||||
|
||||
mCroller.setProgress(mCroller.getProgress());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_DOWN){
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
|
||||
if(!mTapRecognized){
|
||||
if (!mTapRecognized) {
|
||||
|
||||
mTapBpm.addTimestamp(System.currentTimeMillis());
|
||||
mTapRecognized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
|
||||
if(!mTapBpmThread.isAlive() && mTapRecognized){
|
||||
if (!mTapBpmThread.isAlive() && mTapRecognized) {
|
||||
mTapBpmThread.start();
|
||||
mCroller.setLabel("Tippe weiter");
|
||||
}
|
||||
@@ -240,20 +220,11 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
//init GoogleApiClient and get nodeid (phone)
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.build();
|
||||
|
||||
//get display infos
|
||||
mDisplayWidth= this.getResources().getDisplayMetrics().widthPixels;
|
||||
mDisplayHeight= this.getResources().getDisplayMetrics().heightPixels;
|
||||
mDisplayWidth = this.getResources().getDisplayMetrics().widthPixels;
|
||||
mDisplayHeight = this.getResources().getDisplayMetrics().heightPixels;
|
||||
mDisplayCenter = new Point((mDisplayWidth / 2), (mDisplayHeight / 2));
|
||||
|
||||
mModeRecord = false;
|
||||
|
||||
mTapBpm = new TapBpm();
|
||||
mTapBpm.add(this);
|
||||
mTapBpmThread = new Thread(mTapBpm, "tapThread");
|
||||
@@ -261,24 +232,33 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
|
||||
mTextView = (TextView) findViewById(R.id.bpmText);
|
||||
|
||||
mSender = new SensorDataFileSender(this);
|
||||
|
||||
// circular progress bar
|
||||
mCroller = (Croller) findViewById(R.id.croller);
|
||||
mCroller.setOnProgressChangedListener(new Croller.onProgressChangedListener() {
|
||||
@Override
|
||||
public void onProgressChanged(int progress) {
|
||||
// use the progress
|
||||
String curProgress = Integer.toString(progress);
|
||||
String metronomBpm = Integer.toString(mMetronomBpm);;
|
||||
mTextView.setText(curProgress);
|
||||
public void onProgressChanged(int currentBPM) {
|
||||
|
||||
//TODO: too many messages? or just because of thread?
|
||||
if(mModeRecord){
|
||||
sendMessage(START_RECORD_PATH + ":" + metronomBpm + ":" + curProgress);
|
||||
}
|
||||
else {
|
||||
sendMessage(UPDATE_PATH + ":" + metronomBpm + ":" + curProgress);
|
||||
if(currentBPM != mLastSendBPM){
|
||||
|
||||
mTextView.setText(Integer.toString(currentBPM));
|
||||
mSender.sendMessage(UPDATE_PATH + ":" + mMetronomBpm + ":" + currentBPM);
|
||||
mLastSendBPM = currentBPM;
|
||||
}
|
||||
|
||||
if(mReadyToSend){
|
||||
mReadyToSend = false;
|
||||
new java.util.Timer().schedule(
|
||||
new java.util.TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
// your code here
|
||||
|
||||
mReadyToSend = true;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -297,82 +277,67 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
setAmbientEnabled();
|
||||
}
|
||||
|
||||
private void sendMessage(String Key) {
|
||||
|
||||
//TODO: sammel key's und schicke nur die hälfte
|
||||
|
||||
if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
|
||||
//Log.d(TAG, "-- " + mGoogleApiClient.isConnected());
|
||||
Wearable.MessageApi.sendMessage(
|
||||
mGoogleApiClient, mNode.getId(), Key, new byte[0]).setResultCallback(
|
||||
|
||||
new ResultCallback<MessageApi.SendMessageResult>() {
|
||||
@Override
|
||||
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
|
||||
|
||||
//Log.d(TAG, "-- " + sendMessageResult.getStatus().getStatusCode());
|
||||
if (!sendMessageResult.getStatus().isSuccess()) {
|
||||
Log.e(TAG, "Failed to send message with status code: "
|
||||
+ sendMessageResult.getStatus().getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
|
||||
//if record mode is on, we are not able to use the croller
|
||||
if(mModeRecord){
|
||||
if (mModeRecord) {
|
||||
|
||||
//TODO: jedes mal wenn ich touche ruft eine der beiden funktionen was im croller auf und der ruft neu zeichnen auf, was den progresslistener aufruf.
|
||||
//bei extrem vielen berührungen des displays kackt alles ab. er zeichnet auch ständig alles neu
|
||||
boolean changeColor = onColorChanging(ev, Color.parseColor("#EE693F"));
|
||||
return onLongPressCustomized(ev);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
boolean changeColor = onColorChanging(ev, Color.parseColor("#158b69"));
|
||||
boolean superTouch = super.dispatchTouchEvent(ev);
|
||||
boolean longPress = onLongPressCustomized(ev);
|
||||
boolean tapForBpm = onTapForBpm(ev);
|
||||
|
||||
return superTouch || longPress;
|
||||
return superTouch || longPress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentStopped(Vector<Double> bpmList) {
|
||||
|
||||
//TODO: save the bpmList into a file
|
||||
|
||||
//TODO: send bpmList and all other data to phone
|
||||
|
||||
Log.d("FragmentListener", "Received bpmList");
|
||||
public void onFragmentStopped() {
|
||||
//mSender.sendByteArray(sensorData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (!mResolvingError) {
|
||||
mGoogleApiClient.connect();
|
||||
|
||||
//TODO. mGoogleApiClient.disconnect(); implementieren in onPause()
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: rename this! onTapBPMNewEstimation or something like that
|
||||
@Override
|
||||
public void onNewEstimation(int bpm) {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
mSender.wakeUpPhoneCall();
|
||||
//mSender.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
//mSender.unregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTapEstimation(int bpm) {
|
||||
mTapBpmEstimation = bpm;
|
||||
|
||||
synchronized (this) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(mTapBpmEstimation > 0){
|
||||
if (mTapBpmEstimation > 0) {
|
||||
mCroller.setProgress(mTapBpmEstimation);
|
||||
mCroller.setLabel("Fertig");
|
||||
mVibrator.vibrate(10);
|
||||
@@ -382,9 +347,8 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: rename this! onTapBPMFinished or something like that
|
||||
@Override
|
||||
public void onFinished() {
|
||||
public void onTapFinished() {
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -395,55 +359,14 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
|
||||
mTapBpm.clearTimestamps();
|
||||
mTapRecognized = false;
|
||||
/*
|
||||
mTapBpmThread.interrupt();
|
||||
try {
|
||||
mTapBpmThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(@Nullable Bundle bundle) {
|
||||
resolveNode();
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while(mNode == null){
|
||||
try {
|
||||
Thread.sleep(1000L);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
sendMessage(START_ACTIVITY_PATH);
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
//TODO: implement this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
|
||||
//TODO: implement this
|
||||
}
|
||||
|
||||
private void resolveNode() {
|
||||
|
||||
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
|
||||
.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
|
||||
@Override
|
||||
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
|
||||
for (Node node : nodes.getNodes()) {
|
||||
mNode = node;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
* Created by toni on 13/11/17.
|
||||
*/
|
||||
|
||||
//TODO: implement the metronome similar to phone. since thread sleeping is no accurate enough
|
||||
public class Metronome implements Runnable{
|
||||
|
||||
private volatile boolean mRunning = true;
|
||||
@@ -27,7 +28,12 @@ public class Metronome implements Runnable{
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(60000 / mBPM);
|
||||
if(mBPM > 0){
|
||||
Thread.sleep(60000 / mBPM);
|
||||
} else {
|
||||
Thread.sleep(60000);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class TapBpm implements Runnable {
|
||||
mBpmTapped = (int) (60000 / (sumDifferenceMs / (mReceivedTabs.size() - 1)));
|
||||
|
||||
for (TapBpm.OnTapBpmListener listener:listeners) {
|
||||
listener.onNewEstimation(mBpmTapped);
|
||||
listener.onNewTapEstimation(mBpmTapped);
|
||||
}
|
||||
|
||||
//only update everytime a new timestamp arrives
|
||||
@@ -45,7 +45,7 @@ public class TapBpm implements Runnable {
|
||||
|
||||
|
||||
for (TapBpm.OnTapBpmListener listener:listeners) {
|
||||
listener.onFinished();
|
||||
listener.onTapFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ public class TapBpm implements Runnable {
|
||||
* Interface for callback calculated bpm
|
||||
*/
|
||||
public interface OnTapBpmListener {
|
||||
void onFinished();
|
||||
void onNewEstimation(int bpm);
|
||||
void onTapFinished();
|
||||
void onNewTapEstimation(int bpm);
|
||||
}
|
||||
|
||||
private List<TapBpm.OnTapBpmListener> listeners = new CopyOnWriteArrayList<TapBpm.OnTapBpmListener>();
|
||||
|
||||
@@ -22,12 +22,12 @@ import java.util.Vector;
|
||||
public class WorkerFragment extends Fragment implements Metronome.OnMetronomeListener, Estimator.OnBpmEstimatorListener{
|
||||
|
||||
private OnFragmentInteractionListener mListener;
|
||||
private Vector<Double> mBpmList; //TODO save to file.
|
||||
private Vector<Double> mBpmList;
|
||||
private byte[] mSensorData;
|
||||
private boolean mWorkerRunning = false;
|
||||
|
||||
private Estimator mEstimator;
|
||||
private Metronome mMetronome;
|
||||
|
||||
//private Thread mBpmThread;
|
||||
private Thread mMetronomeThread;
|
||||
|
||||
private Vibrator mVibrator;
|
||||
@@ -58,13 +58,12 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
|
||||
// init bpm estimator and listener
|
||||
mEstimator = new Estimator(getContext());
|
||||
mEstimator.add(this);
|
||||
//mBpmThread = new Thread(mEstimator, "estThread");
|
||||
mBpmList = new Vector<Double>();
|
||||
|
||||
// init metronome and listener
|
||||
mMetronome = new Metronome(getArguments().getInt("bpm"));
|
||||
mMetronome.add(this);
|
||||
mMetronomeThread = new Thread(mMetronome, "estThread");
|
||||
mMetronomeThread = new Thread(mMetronome, "metronomThread");
|
||||
|
||||
mVibrator = (Vibrator) this.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
|
||||
@@ -99,27 +98,26 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
|
||||
|
||||
// start the worker thread for metronom
|
||||
mMetronomeThread.start();
|
||||
|
||||
// everything is running
|
||||
mWorkerRunning = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
mWorkerRunning = false;
|
||||
|
||||
// stop the worker thread for bpm estimator
|
||||
mEstimator.stop();
|
||||
|
||||
// stop the worker thread for metronom
|
||||
mMetronome.stop();
|
||||
mMetronomeThread.interrupt();
|
||||
try {
|
||||
mMetronomeThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//private listener with list of all estimated bpm
|
||||
if (mListener != null) {
|
||||
mListener.onFragmentStopped(mBpmList);
|
||||
mListener.onFragmentStopped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -147,33 +145,43 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
|
||||
|
||||
//TODO: what if multiple threads access mBpmList? put into synchronized? @frank fragen :D
|
||||
//TODO: send this to smartphone
|
||||
if(mWorkerRunning){
|
||||
if(bpm == -1){
|
||||
//to stuff with UI. Write Text or make XX or something like that
|
||||
}
|
||||
|
||||
if(bpm == -1){
|
||||
//to stuff with UI. Write Text or make XX or something like that
|
||||
mBpmList.add(bpm);
|
||||
|
||||
// we need this here, since ui elements can only be changed within activity thread and
|
||||
// onNewDataAvailable lives inside the bpm estimation thread.
|
||||
// TODO: is this really okay? also synchronized?
|
||||
|
||||
if(getActivity() != null) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int tmpBPM = (int) (Math.round(mBpmList.lastElement()));
|
||||
mTextView.setText(String.valueOf(tmpBPM));
|
||||
mCroller.setProgress(tmpBPM);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBpmList.add(bpm);
|
||||
|
||||
// we need this here, since ui elements can only be changed within activity thread and
|
||||
// onNewDataAvailable lives inside the bpm estimation thread.
|
||||
// TODO: is this really okay? also synchronized?
|
||||
synchronized (this) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
mTextView.setText(String.valueOf(mBpmList.lastElement()));
|
||||
mCroller.setProgress((int)(Math.round(mBpmList.lastElement())));
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Callback function for the sensordata recorded while estimating bpm
|
||||
*/
|
||||
@Override
|
||||
public void onEstimationStopped(byte[] sensorData) {
|
||||
//mSensorData = sensorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for connecting to WorkerFragment
|
||||
*/
|
||||
public interface OnFragmentInteractionListener {
|
||||
void onFragmentStopped(Vector<Double> bpmList);
|
||||
void onFragmentStopped();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@ public class AccelerometerData {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public AccelerometerData(AccelerometerData other){
|
||||
this.ts = other.ts;
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
this.z = other.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other){
|
||||
|
||||
|
||||
@@ -20,19 +20,20 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
|
||||
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
||||
public boolean add(AccelerometerData ad){
|
||||
synchronized (this){
|
||||
//do not add duplicates!
|
||||
if (!isEmpty() && getYongest().equals(ad)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//do not add duplicates!
|
||||
if(!isEmpty() && getYongest().equals(ad)){
|
||||
return false;
|
||||
boolean r = super.add(ad);
|
||||
if (size() > mWindowSize) {
|
||||
removeRange(0, size() - mWindowSize);
|
||||
}
|
||||
|
||||
++mOverlapCounter;
|
||||
return r;
|
||||
}
|
||||
|
||||
boolean r = super.add(ad);
|
||||
if (size() > mWindowSize){
|
||||
removeRange(0, size() - mWindowSize);
|
||||
}
|
||||
|
||||
++mOverlapCounter;
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean isNextWindowReady(){
|
||||
@@ -44,6 +45,16 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public AccelerometerWindowBuffer getFixedWindow(){
|
||||
AccelerometerWindowBuffer other = new AccelerometerWindowBuffer(mWindowSize, mOverlapSize);
|
||||
synchronized (this){
|
||||
for(AccelerometerData data : this){
|
||||
other.add(new AccelerometerData(data));
|
||||
}
|
||||
}
|
||||
return other;
|
||||
}
|
||||
|
||||
public AccelerometerData getYongest() {
|
||||
return get(size() - 1);
|
||||
}
|
||||
|
||||
@@ -44,14 +44,15 @@ public class BpmEstimator {
|
||||
//mKalman = new SimpleKalman();
|
||||
}
|
||||
|
||||
public double estimate(){
|
||||
//TODO: we use the buffer from outside.. this buffer is continuously updated.. not good!
|
||||
public double estimate(AccelerometerWindowBuffer fixedWindow){
|
||||
|
||||
double sampleRate = mSampleRate_ms;
|
||||
if(sampleRate <= 0){
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(mBuffer.getTs())));
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(fixedWindow.getTs())));
|
||||
}
|
||||
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(mBuffer, sampleRate);
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(fixedWindow, sampleRate);
|
||||
|
||||
//are we conducting?
|
||||
//just look at the newest 512 samples
|
||||
@@ -90,6 +91,8 @@ public class BpmEstimator {
|
||||
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize() * sampleRate));
|
||||
if(++mResetCounter > resetAfter){
|
||||
mBpmHistory.clear();
|
||||
|
||||
//TODO: send signal to clear.
|
||||
mBuffer.clear();
|
||||
mMvg.clear();
|
||||
mResetCounter = 0;
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package de.tonifetzer.conductorswatch.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.wearable.ChannelClient;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by toni on 15/01/18.
|
||||
*/
|
||||
|
||||
public class SensorDataFileSender {
|
||||
|
||||
private static final String START_ACTIVITY_PATH = "/start-activity";
|
||||
public static final String TAG = "SensorDataFileSender";
|
||||
private static final String DATA_PATH ="/data";
|
||||
|
||||
private Node mNode;
|
||||
private boolean mChannelOpenend;
|
||||
private ChannelClient.ChannelCallback mSensorDataChannelCallback;
|
||||
private Context mContext;
|
||||
|
||||
public SensorDataFileSender(Context context){
|
||||
|
||||
mContext = context;
|
||||
mChannelOpenend = false;
|
||||
mSensorDataChannelCallback = new ChannelClient.ChannelCallback() {
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(@NonNull ChannelClient.Channel channel, int i, int i1) {
|
||||
super.onChannelClosed(channel, i, i1);
|
||||
//node: this happens in main thread, since anonymous abstract, to prevent make new class extending ChannelClient.ChannelCallback
|
||||
|
||||
mChannelOpenend = false;
|
||||
Log.d(TAG, "Channel is closed on wear");
|
||||
|
||||
//stop showing something on saving screen :D
|
||||
}
|
||||
};
|
||||
|
||||
//find nearest smartwatch
|
||||
getNearestPhone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends a whole SensorDataFile within a byte array.
|
||||
* @param sensorData
|
||||
*/
|
||||
public void sendByteArray(byte[] sensorData) {
|
||||
|
||||
if(!mChannelOpenend){
|
||||
//open channel
|
||||
Wearable.getChannelClient(mContext).openChannel(mNode.getId(), DATA_PATH).addOnSuccessListener((ChannelClient.Channel openChannel) -> {
|
||||
Log.d(TAG, "Data channel to phone successfully opened.");
|
||||
mChannelOpenend = true;
|
||||
|
||||
Wearable.getChannelClient(mContext).getOutputStream(openChannel).addOnSuccessListener((OutputStream stream) -> {
|
||||
|
||||
synchronized (stream){
|
||||
//write the data into the google service api cloud or whatever that is
|
||||
try {
|
||||
stream.write(sensorData);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
Log.d(TAG, "Data send and stream closed." );
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}).addOnFailureListener((Exception e) -> {
|
||||
Log.d(TAG, "Unable to get outputstream : " + e);
|
||||
});
|
||||
|
||||
}).addOnFailureListener((Exception e) -> {
|
||||
Log.d(TAG, "Unable to open data channel to phone: " + e);
|
||||
});
|
||||
|
||||
|
||||
// Show something on watch "saving" screen
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Channel is still open, that means the phone still needs time to save stuff from last recording!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void sendMessage(String Key) {
|
||||
|
||||
if (mNode != null) {
|
||||
|
||||
Task<Integer> sendTask = Wearable.getMessageClient(mContext).sendMessage(mNode.getId(), Key, new byte[0]);
|
||||
sendTask.addOnSuccessListener((Integer i) -> {
|
||||
//Log.d(TAG, "Sending msg success: " + i);
|
||||
}).addOnFailureListener((Exception e) -> {
|
||||
Log.d(TAG, "Sending msg failed with error: " + e);
|
||||
});
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Phone not found!");
|
||||
//search again for phones within the area
|
||||
//TODO: implement this using a timer. search every 10 seconds or something
|
||||
//getNearestPhone();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void getNearestPhone() {
|
||||
|
||||
mNode = null;
|
||||
|
||||
Task<List<Node>> nodeTask = Wearable.getNodeClient(mContext).getConnectedNodes();
|
||||
nodeTask.addOnCompleteListener((Task<List<Node>> task) -> {
|
||||
|
||||
List<Node> nodes = task.getResult();
|
||||
Node bestNodeId = null;
|
||||
|
||||
//TODO: das ist humbug, bestnodid wird immergenommen nie nearby
|
||||
for (Node node : nodes) {
|
||||
if (node.isNearby()) {
|
||||
mNode = node;
|
||||
}
|
||||
bestNodeId = node;
|
||||
}
|
||||
mNode = bestNodeId;
|
||||
|
||||
wakeUpPhoneCall();
|
||||
});
|
||||
}
|
||||
|
||||
public void wakeUpPhoneCall(){
|
||||
//start the app on smartphone
|
||||
if(mNode != null){
|
||||
sendMessage(START_ACTIVITY_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
public void register(){
|
||||
Wearable.getChannelClient(mContext).registerChannelCallback(mSensorDataChannelCallback);
|
||||
}
|
||||
|
||||
public void unregister(){
|
||||
Wearable.getChannelClient(mContext).unregisterChannelCallback(mSensorDataChannelCallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package de.tonifetzer.conductorswatch.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.wearable.ChannelClient;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Created by toni on 15/01/18.
|
||||
*/
|
||||
|
||||
|
||||
public class SensorDataFileStreamer implements Runnable{
|
||||
|
||||
private static final String TAG = "SensorDataFileStreamer";
|
||||
private static final String DATA_PATH ="/data";
|
||||
|
||||
private volatile boolean mRunning = true;
|
||||
private Node mNode;
|
||||
private boolean mChannelOpenend;
|
||||
private boolean mOutputStreamOpened;
|
||||
private ChannelClient.ChannelCallback mSensorDataChannelCallback;
|
||||
private Context mContext;
|
||||
private OutputStream mOutputStream;
|
||||
private final BlockingQueue<byte[]> mQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
public SensorDataFileStreamer(Context context){
|
||||
|
||||
mContext = context;
|
||||
mChannelOpenend = false;
|
||||
mOutputStreamOpened = false;
|
||||
|
||||
mSensorDataChannelCallback = new ChannelClient.ChannelCallback() {
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(@NonNull ChannelClient.Channel channel, int i, int i1) {
|
||||
super.onChannelClosed(channel, i, i1);
|
||||
//node: this happens in main thread, since anonymous abstract, to prevent make new class extending ChannelClient.ChannelCallback
|
||||
|
||||
mChannelOpenend = false;
|
||||
mOutputStreamOpened = false;
|
||||
Log.d(TAG, "Channel is closed on wear");
|
||||
|
||||
Wearable.getChannelClient(mContext).unregisterChannelCallback(mSensorDataChannelCallback);
|
||||
}
|
||||
};
|
||||
|
||||
//find nearest smartwatch and open channel
|
||||
getNearestPhone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while (mRunning) {
|
||||
if (mOutputStreamOpened && mChannelOpenend) {
|
||||
try {
|
||||
|
||||
//TODO: einfach bytestreamwriter direkt hier implementieren. also moutputstream in datastream(moutputstream) und dann direkt über writeint.
|
||||
|
||||
//TODO: channel is closed before stream was finished
|
||||
mOutputStream.write(mQueue.take());
|
||||
|
||||
//TODO: flush after specific file size
|
||||
//mOutputStream.flush();
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
mOutputStreamOpened = false;
|
||||
//TODO: do something here!
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
//open();
|
||||
//TODO: check somewhere if we have a phone or try to reconnect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends a byte array.
|
||||
* @param sensorData
|
||||
*/
|
||||
public void sendByteArray(byte[] sensorData) {
|
||||
if(mRunning && mOutputStreamOpened && mChannelOpenend) {
|
||||
mQueue.offer(sensorData);
|
||||
}
|
||||
}
|
||||
|
||||
private void getNearestPhone() {
|
||||
|
||||
mNode = null;
|
||||
|
||||
Task<List<Node>> nodeTask = Wearable.getNodeClient(mContext).getConnectedNodes();
|
||||
nodeTask.addOnCompleteListener((Task<List<Node>> task) -> {
|
||||
|
||||
List<Node> nodes = task.getResult();
|
||||
Node bestNodeId = null;
|
||||
|
||||
for (Node node : nodes) {
|
||||
if (node.isNearby()) {
|
||||
bestNodeId = node;
|
||||
break;
|
||||
}
|
||||
bestNodeId = node;
|
||||
}
|
||||
mNode = bestNodeId;
|
||||
|
||||
if(mNode != null){
|
||||
open();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void open(){
|
||||
|
||||
if(!mChannelOpenend && mNode != null){
|
||||
|
||||
//register callback
|
||||
Wearable.getChannelClient(mContext).registerChannelCallback(mSensorDataChannelCallback);
|
||||
|
||||
//open channel
|
||||
Wearable.getChannelClient(mContext).openChannel(mNode.getId(), DATA_PATH).addOnSuccessListener((ChannelClient.Channel openChannel) -> {
|
||||
Log.d(TAG, "Data channel to phone successfully opened.");
|
||||
mChannelOpenend = true;
|
||||
|
||||
Wearable.getChannelClient(mContext).getOutputStream(openChannel).addOnSuccessListener((OutputStream stream) -> {
|
||||
mOutputStream = stream;
|
||||
mOutputStreamOpened = true;
|
||||
|
||||
}).addOnFailureListener((Exception e) -> {
|
||||
Log.d(TAG, "Unable to get outputstream : " + e);
|
||||
});
|
||||
|
||||
}).addOnFailureListener((Exception e) -> {
|
||||
Log.d(TAG, "Unable to open data channel to phone: " + e);
|
||||
});
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Channel is still open, that means the phone still needs time to save stuff from last recording!");
|
||||
}
|
||||
}
|
||||
|
||||
public void close(){
|
||||
try {
|
||||
mRunning = false;
|
||||
mOutputStream.close();
|
||||
mOutputStreamOpened = false;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.tonifetzer.conductorswatch.utilities;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -8,20 +10,23 @@ import java.io.IOException;
|
||||
* Created by toni on 20/12/17.
|
||||
*/
|
||||
|
||||
public class FileWriter {
|
||||
|
||||
private String mFilename;
|
||||
public class ByteStreamWriter {
|
||||
|
||||
private ByteArrayOutputStream mByteStream;
|
||||
private DataOutputStream mDataStream;
|
||||
|
||||
FileWriter(String filename){
|
||||
mFilename = filename;
|
||||
private final long mFirstTimestamp;
|
||||
int mDataCounter;
|
||||
|
||||
public ByteStreamWriter(){
|
||||
mByteStream = new ByteArrayOutputStream();
|
||||
mDataStream = new DataOutputStream(mByteStream);
|
||||
|
||||
mDataCounter = 0;
|
||||
mFirstTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void writeDouble(double data){
|
||||
private void writeDouble(double data){
|
||||
try {
|
||||
mDataStream.writeDouble(data);
|
||||
} catch (IOException e) {
|
||||
@@ -29,15 +34,15 @@ public class FileWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeString(String data){
|
||||
private void writeChar(char data){
|
||||
try {
|
||||
mDataStream.writeChars(data);
|
||||
mDataStream.writeChar(data);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeInt(int data){
|
||||
private void writeInt(int data){
|
||||
try {
|
||||
mDataStream.writeInt(data);
|
||||
} catch (IOException e) {
|
||||
@@ -45,12 +50,30 @@ public class FileWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getByteArray(){
|
||||
return mByteStream.toByteArray();
|
||||
private void writeLong(long data){
|
||||
try {
|
||||
mDataStream.writeLong(data);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getFilename(){
|
||||
return mFilename;
|
||||
public void writeSensor3D(int type, double x, double y, double z){
|
||||
synchronized (this){
|
||||
long writeTime = System.currentTimeMillis() - mFirstTimestamp;
|
||||
writeLong(writeTime);
|
||||
writeInt(type);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
|
||||
++mDataCounter;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getByteArray(){
|
||||
//Log.d("DataLayerListenerService", "Num data written: " + mDataCounter);
|
||||
return mByteStream.toByteArray();
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
@@ -61,6 +84,8 @@ public class FileWriter {
|
||||
try {
|
||||
mByteStream.close();
|
||||
mDataStream.close();
|
||||
|
||||
mDataCounter = 0;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="240"
|
||||
android:text="80"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#158b69"
|
||||
android:textSize="40sp"/>
|
||||
|
||||
Reference in New Issue
Block a user