diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 6510e78d8..095d097da 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -43,6 +43,7 @@ limitations under the License.
+
diff --git a/src/main/java/de/dennisguse/opentracks/TrackController.java b/src/main/java/de/dennisguse/opentracks/TrackController.java
index e1a0aba1f..877b19fea 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackController.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackController.java
@@ -16,15 +16,21 @@
package de.dennisguse.opentracks;
+import android.annotation.SuppressLint;
import android.app.Activity;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Handler;
+import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
+import android.widget.Toast;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
+import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.UnitConversions;
@@ -46,6 +52,7 @@ public class TrackController {
private final ImageButton recordImageButton;
private final ImageButton stopImageButton;
private final boolean alwaysShow;
+ private ButtonDelay buttonDelay;
private boolean isRecording;
private boolean isPaused;
@@ -65,6 +72,7 @@ public class TrackController {
}
};
+ @SuppressLint("ClickableViewAccessibility")
TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
this.activity = activity;
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
@@ -75,10 +83,85 @@ public class TrackController {
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
recordImageButton = activity.findViewById(R.id.track_controller_record);
- recordImageButton.setOnClickListener(recordListener);
+ recordImageButton.setOnTouchListener((view, motionEvent) -> onRecordTouch(activity, recordListener, motionEvent));
stopImageButton = activity.findViewById(R.id.track_controller_stop);
- stopImageButton.setOnClickListener(stopListener);
+ stopImageButton.setOnTouchListener((view, motionEvent) -> onStopTouch(activity, stopListener, motionEvent));
+ }
+
+ private boolean onRecordTouch(final Activity activity, final OnClickListener recordListener, final MotionEvent motionEvent) {
+ if (isRecording && !isPaused) {
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ buttonDelay = new ButtonDelay(activity, recordImageButton, R.drawable.ic_button_pause_anim, R.string.hold_to_pause, recordListener);
+ new Thread(buttonDelay).start();
+ return true;
+ } else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
+ recordImageButton.setImageResource(R.drawable.ic_button_pause);
+ if (buttonDelay != null) {
+ buttonDelay.canceled = true;
+ }
+ return true;
+ }
+ } else if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ recordListener.onClick(null);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onStopTouch(final Activity activity, final OnClickListener stopListener, final MotionEvent motionEvent) {
+ if (isRecording) {
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+ buttonDelay = new ButtonDelay(activity, stopImageButton, R.drawable.ic_button_stop_anim, R.string.hold_to_stop, stopListener);
+ new Thread(buttonDelay).start();
+ return true;
+ } else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
+ stopImageButton.setImageResource(R.drawable.ic_button_stop);
+ if (buttonDelay != null) {
+ buttonDelay.canceled = true;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class ButtonDelay implements Runnable {
+
+ private boolean canceled = false;
+
+ private final ImageButton imageButton;
+ private final Activity activity;
+ private final OnClickListener clickListener;
+ private final int delayMillis;
+ private final AnimatedVectorDrawable animatedDrawable;
+ private final int delayMessageId;
+
+ private ButtonDelay(final Activity activity, final ImageButton imageButton, final int animDrawableId, final int delayMessageId, final OnClickListener clickListener) {
+ this.activity = activity;
+ this.clickListener = clickListener;
+ this.delayMillis = activity.getResources().getInteger(R.integer.buttonDelayMillis);
+ this.imageButton = imageButton;
+ this.animatedDrawable = (AnimatedVectorDrawable) activity.getDrawable(animDrawableId);
+ this.delayMessageId = delayMessageId;
+ }
+
+ @Override
+ public void run() {
+ imageButton.setImageDrawable(animatedDrawable);
+ activity.runOnUiThread(animatedDrawable::start);
+
+ ActivityUtils.vibrate(activity, 150);
+ activity.runOnUiThread(()-> ActivityUtils.toast(activity, delayMessageId, Toast.LENGTH_SHORT, Gravity.TOP));
+ try {
+ Thread.sleep(delayMillis);
+ } catch (InterruptedException ignored) {
+ }
+ if (!canceled) {
+ activity.runOnUiThread(()-> clickListener.onClick(null));
+ ActivityUtils.vibrate(activity, 1000);
+ }
+ }
}
public void update(boolean recording, boolean paused) {
@@ -95,10 +178,10 @@ public class TrackController {
return;
}
- recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
+ recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
- stopImageButton.setImageResource(isRecording ? R.drawable.button_stop : R.drawable.ic_button_stop_disabled);
+ stopImageButton.setImageResource(isRecording ? R.drawable.ic_button_stop : R.drawable.ic_button_stop_disabled);
stopImageButton.setEnabled(isRecording);
statusTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
diff --git a/src/main/java/de/dennisguse/opentracks/util/ActivityUtils.java b/src/main/java/de/dennisguse/opentracks/util/ActivityUtils.java
index 21d015ee4..bcf0d638b 100644
--- a/src/main/java/de/dennisguse/opentracks/util/ActivityUtils.java
+++ b/src/main/java/de/dennisguse/opentracks/util/ActivityUtils.java
@@ -4,13 +4,18 @@ import android.app.Activity;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
+import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AbsListView;
import android.widget.ListView;
+import android.widget.Toast;
import androidx.appcompat.widget.SearchView;
@@ -21,6 +26,8 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.SearchListActivity;
import de.dennisguse.opentracks.TrackController;
+import static android.content.Context.VIBRATOR_SERVICE;
+
public class ActivityUtils {
private static final String TAG = ActivityUtils.class.getSimpleName();
@@ -129,4 +136,20 @@ public class ActivityUtils {
}
});
}
+
+ public static void vibrate(Activity activity, int milliseconds) {
+ final Vibrator vibrator = (Vibrator) activity.getSystemService(VIBRATOR_SERVICE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ vibrator.vibrate(VibrationEffect.createOneShot(milliseconds, VibrationEffect.DEFAULT_AMPLITUDE));
+ } else {
+ vibrator.vibrate(milliseconds);
+ }
+ }
+
+ public static void toast(final Activity activity, final int resId, final int duration, final int gravity) {
+ final Toast toast = Toast.makeText(activity, resId, duration);
+ toast.setGravity(gravity, 0, 0);
+ toast.show();
+ }
+
}
diff --git a/src/main/res/animator/button_animator.xml b/src/main/res/animator/button_animator.xml
new file mode 100644
index 000000000..2f3e87783
--- /dev/null
+++ b/src/main/res/animator/button_animator.xml
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/src/main/res/drawable/button_pause.xml b/src/main/res/drawable/button_pause.xml
deleted file mode 100644
index 1e1dbc151..000000000
--- a/src/main/res/drawable/button_pause.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/drawable/button_stop.xml b/src/main/res/drawable/button_stop.xml
deleted file mode 100644
index bf5bf445e..000000000
--- a/src/main/res/drawable/button_stop.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/drawable/ic_button_pause.xml b/src/main/res/drawable/ic_button_pause.xml
index 1a3b62da5..3dc7e0814 100644
--- a/src/main/res/drawable/ic_button_pause.xml
+++ b/src/main/res/drawable/ic_button_pause.xml
@@ -24,4 +24,9 @@
android:strokeWidth="4.82273865"
android:strokeAlpha="1"
android:strokeColor="#00000000" />
+
+
diff --git a/src/main/res/drawable/ic_button_pause_anim.xml b/src/main/res/drawable/ic_button_pause_anim.xml
new file mode 100644
index 000000000..87e111c08
--- /dev/null
+++ b/src/main/res/drawable/ic_button_pause_anim.xml
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/res/drawable/ic_button_pause_pressed.xml b/src/main/res/drawable/ic_button_pause_pressed.xml
deleted file mode 100644
index 681abde8b..000000000
--- a/src/main/res/drawable/ic_button_pause_pressed.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
diff --git a/src/main/res/drawable/ic_button_stop.xml b/src/main/res/drawable/ic_button_stop.xml
index 1910e18b6..4937530c7 100644
--- a/src/main/res/drawable/ic_button_stop.xml
+++ b/src/main/res/drawable/ic_button_stop.xml
@@ -25,4 +25,9 @@
android:strokeWidth="1.04023623"
android:strokeAlpha="1"
android:strokeColor="#414141" />
+
+
diff --git a/src/main/res/drawable/ic_button_stop_anim.xml b/src/main/res/drawable/ic_button_stop_anim.xml
new file mode 100644
index 000000000..a389c2136
--- /dev/null
+++ b/src/main/res/drawable/ic_button_stop_anim.xml
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/res/drawable/ic_button_stop_pressed.xml b/src/main/res/drawable/ic_button_stop_pressed.xml
deleted file mode 100644
index a53a41a4f..000000000
--- a/src/main/res/drawable/ic_button_stop_pressed.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml
index c10ce3e96..b8faa8f6a 100644
--- a/src/main/res/values/settings.xml
+++ b/src/main/res/values/settings.xml
@@ -231,4 +231,6 @@
true
settingsRecordingKey
+
+ 1500
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 825d22db5..561155dbc 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -584,4 +584,7 @@ limitations under the License.
I would like to share an image with you
I think you might be interested in this image.
+
+ Hold to stop recording
+ Hold to pause recording