From 96abc0a4daa4e4dacf1ab966eb8377bf43a8ac4b Mon Sep 17 00:00:00 2001 From: toni Date: Mon, 20 Nov 2017 21:20:08 +0100 Subject: [PATCH] closes #12 - implemented tap to estimate bpm --- .../tonifetzer/conductorswatch/Croller.java | 14 ++- .../conductorswatch/MainActivity.java | 85 +++++++++++++++++-- .../de/tonifetzer/conductorswatch/TapBpm.java | 71 ++++++++++++++++ .../conductorswatch/WorkerFragment.java | 4 - .../app/src/main/res/values/attrs.xml | 30 +++++++ 5 files changed, 191 insertions(+), 13 deletions(-) create mode 100644 android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/TapBpm.java create mode 100644 android/ConductorsWatch/app/src/main/res/values/attrs.xml diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/Croller.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/Croller.java index dda1abb..f72a935 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/Croller.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/Croller.java @@ -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; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/MainActivity.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/MainActivity.java index ce60c81..a8d9bb5 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/MainActivity.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/MainActivity.java @@ -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(); + } + } } diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/TapBpm.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/TapBpm.java new file mode 100644 index 0000000..295119b --- /dev/null +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/TapBpm.java @@ -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 mReceivedTabs = new Vector(); + 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 listeners = new CopyOnWriteArrayList(); + public void add(TapBpm.OnTapBpmListener listener){listeners.add(listener);} + public void remove(TapBpm.OnTapBpmListener listener){listeners.remove(listener);} +} diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java index fb9436c..4ec9cfe 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java @@ -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; diff --git a/android/ConductorsWatch/app/src/main/res/values/attrs.xml b/android/ConductorsWatch/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..3f2c012 --- /dev/null +++ b/android/ConductorsWatch/app/src/main/res/values/attrs.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file