closes #12 - implemented tap to estimate bpm

This commit is contained in:
toni
2017-11-20 21:20:08 +01:00
parent cfa7f84432
commit 96abc0a4da
5 changed files with 191 additions and 13 deletions

View File

@@ -49,7 +49,7 @@ public class Croller extends View {
private float indicatorWidth = 7;
private String label = "Label";
private int labelSize = 40;
private int labelSize = 20;
private int labelColor = Color.WHITE;
private int startOffset = 30;
@@ -391,7 +391,7 @@ public class Croller extends View {
canvas.drawCircle(midx, midy, backCircleRadius, circlePaint);
circlePaint.setColor(mainCircleColor);
canvas.drawCircle(midx, midy, mainCircleRadius, circlePaint);
canvas.drawText(label, midx, midy + (float) (radius * 1.1), textPaint);
canvas.drawText(label, midx, midy + (float) (radius * 0.9), textPaint);
canvas.drawLine(x1, y1, x2, y2, linePaint);
}
}
@@ -577,11 +577,17 @@ public class Croller extends View {
}
public boolean isMainCircleAnimationRunning(){
return mainCircleAnimation.isRunning();
if(mainCircleAnimation != null){
return mainCircleAnimation.isRunning();
}
return false;
}
public boolean isBackCircleAnimationRunning(){
return backCircleAnimation.isRunning();
if(backCircleAnimation != null){
return backCircleAnimation.isRunning();
}
return false;
}
private ValueAnimator backCircleAnimation;

View File

@@ -13,11 +13,12 @@ import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.TextView;
import java.sql.Time;
import java.util.Vector;
import de.tonifetzer.conductorswatch.utilities.Utils;
public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener{
public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener, TapBpm.OnTapBpmListener{
// member
private TextView mTextView;
@@ -34,7 +35,11 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
private int mMetronomBpm;
// tapping
private int mReceivedTabs;
private Thread mTapBpmThread;
private TapBpm mTapBpm;
private boolean mTapRecognized = false;
private int mTapBpmEstimation;
private Vibrator mVibrator;
//parameter for long press to start the worker
private Point mPreviousMovePoint;
@@ -161,9 +166,36 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
public boolean onTapForBpm(MotionEvent ev){
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
//only works within the maincircle of the scroller
float distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
return true;
mCroller.setProgress(mCroller.getProgress());
return false;
}
if(ev.getAction() == MotionEvent.ACTION_DOWN){
if(!mTapRecognized){
mTapBpm.addTimestamp(System.currentTimeMillis());
mTapRecognized = true;
}
}
if(ev.getAction() == MotionEvent.ACTION_UP) {
if(!mTapBpmThread.isAlive()){
mTapBpmThread.start();
mCroller.setLabel("Tippe weiter");
}
mTapRecognized = false;
}
return false;
}
@Override
@@ -177,6 +209,11 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
mModeRecord = false;
mTapBpm = new TapBpm();
mTapBpm.add(this);
mTapBpmThread = new Thread(mTapBpm, "tapThread");
mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mTextView = (TextView) findViewById(R.id.bpmText);
// circular progress bar
@@ -185,7 +222,6 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
@Override
public void onProgressChanged(int progress) {
// use the progress
//Log.d("Progress: ", String.valueOf(progress));
mTextView.setText(String.valueOf(progress));
}
});
@@ -217,7 +253,11 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
else {
boolean changeColor = onColorChanging(ev, Color.parseColor("#158b69"));
return onLongPressCustomized(ev) || super.dispatchTouchEvent(ev);
boolean superTouch = super.dispatchTouchEvent(ev);
boolean longPress = onLongPressCustomized(ev);
boolean tapForBpm = onTapForBpm(ev);
return superTouch || longPress;
}
}
@@ -232,5 +272,40 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
Log.d("FragmentListener", "Received bpmList");
}
@Override
public void onNewEstimation(int bpm) {
mTapBpmEstimation = bpm;
synchronized (this) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mTapBpmEstimation > 0){
mCroller.setProgress(mTapBpmEstimation);
mCroller.setLabel("Fertig");
mVibrator.vibrate(10);
}
}
});
}
}
@Override
public void onFinished() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mCroller.setLabel("");
}
});
mTapBpm.clearTimestamps();
mTapBpmThread.interrupt();
try {
mTapBpmThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,71 @@
package de.tonifetzer.conductorswatch;
import android.util.Log;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by toni on 20/11/17.
*/
public class TapBpm implements Runnable {
private Vector<Long> mReceivedTabs = new Vector<Long>();
private int mBpmTapped;
@Override
public void run() {
int breakCounter = 2000;
mBpmTapped = 0;
int calcNewBpmCounter = 3;
do{
if(mBpmTapped > 0){
breakCounter = 2 * (60000 / mBpmTapped);
}
if (mReceivedTabs.size() > calcNewBpmCounter) {
Long sumDifferenceMs = 0l;
for (int i = 0; i < mReceivedTabs.size() -1; ++i) {
sumDifferenceMs += mReceivedTabs.get(i + 1)- mReceivedTabs.get(i);
}
mBpmTapped = (int) (60000 / (sumDifferenceMs / (mReceivedTabs.size() - 1)));
for (TapBpm.OnTapBpmListener listener:listeners) {
listener.onNewEstimation(mBpmTapped);
}
//only update everytime a new timestamp arrives
++calcNewBpmCounter;
}
}while(System.currentTimeMillis() - mReceivedTabs.lastElement() < breakCounter);
for (TapBpm.OnTapBpmListener listener:listeners) {
listener.onFinished();
}
}
public void addTimestamp(Long ts){
mReceivedTabs.add(ts);
}
public void clearTimestamps(){
mReceivedTabs.clear();
}
/**
* Interface for callback calculated bpm
*/
public interface OnTapBpmListener {
void onFinished();
void onNewEstimation(int bpm);
}
private List<TapBpm.OnTapBpmListener> listeners = new CopyOnWriteArrayList<TapBpm.OnTapBpmListener>();
public void add(TapBpm.OnTapBpmListener listener){listeners.add(listener);}
public void remove(TapBpm.OnTapBpmListener listener){listeners.remove(listener);}
}

View File

@@ -2,16 +2,12 @@ package de.tonifetzer.conductorswatch;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.os.Bundle;
import android.app.Fragment;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import java.util.Vector;

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Croller">
<attr name="progress" format="integer" />
<attr name="label" format="string" />
<attr name="back_circle_color" format="color" />
<attr name="main_circle_color" format="color" />
<attr name="indicator_color" format="color" />
<attr name="progress_primary_color" format="color" />
<attr name="progress_secondary_color" format="color" />
<attr name="label_size" format="integer" />
<attr name="label_color" format="color" />
<attr name="indicator_width" format="float" />
<attr name="is_continuous" format="boolean" />
<attr name="progress_primary_circle_size" format="float" />
<attr name="progress_secondary_circle_size" format="float" />
<attr name="progress_primary_stroke_width" format="float" />
<attr name="progress_secondary_stroke_width" format="float" />
<attr name="sweep_angle" format="integer" />
<attr name="start_offset" format="integer" />
<attr name="max" format="integer" />
<attr name="min" format="integer" />
<attr name="main_circle_radius" format="float" />
<attr name="back_circle_radius" format="float" />
<attr name="progress_radius" format="float" />
<attr name="touch_circle_radius_max" format="float" />
<attr name="touch_circle_radius_min" format="float" />
<attr name="anticlockwise" format="boolean" />
</declare-styleable>
</resources>