diff --git a/src/main/java/de/dennisguse/opentracks/TrackController.java b/src/main/java/de/dennisguse/opentracks/TrackController.java
index 0a72f0abf..641baf4a2 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackController.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackController.java
@@ -18,17 +18,12 @@ package de.dennisguse.opentracks;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
import android.os.Handler;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageButton;
-import android.widget.Toast;
-
-import androidx.core.content.ContextCompat;
import de.dennisguse.opentracks.databinding.TrackControllerBinding;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
@@ -42,26 +37,29 @@ import de.dennisguse.opentracks.util.UnitConversions;
*
* @author Jimmy Shih
*/
-public class TrackController {
+public class TrackController implements View.OnTouchListener {
private static final String TAG = TrackController.class.getSimpleName();
private final Activity activity;
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
- private final Handler handlerUpdateTotalTime;
+ private final Handler handlerUpdateTotalTime = new Handler();
private final boolean alwaysShow;
- private ButtonDelay buttonDelay;
+
+ private Runnable buttonDelay;
+ private final int buttonDelayDuration;
private final TrackControllerBinding viewBinding;
+ private final TransitionDrawable transition;
private boolean isRecording;
private boolean isPaused;
private long totalTime = 0;
-
private long totalTimeTimestamp = 0;
-
private boolean isResumed = false;
+ private final Callback callback;
+
// A runnable to update the total time.
private final Runnable updateTotalTimeRunnable = new Runnable() {
public void run() {
@@ -73,98 +71,98 @@ public class TrackController {
};
@SuppressLint("ClickableViewAccessibility")
- TrackController(Activity activity, TrackControllerBinding viewBinding, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
+ TrackController(Activity activity, TrackControllerBinding viewBinding, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, Callback callback) {
this.activity = activity;
this.viewBinding = viewBinding;
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
this.alwaysShow = alwaysShow;
- handlerUpdateTotalTime = new Handler();
- viewBinding.trackControllerRecord.setOnTouchListener((view, motionEvent) -> onRecordTouch(activity, recordListener, motionEvent));
+ this.callback = callback;
- viewBinding.trackControllerStop.setOnTouchListener((view, motionEvent) -> onStopTouch(activity, stopListener, motionEvent));
+ buttonDelayDuration = activity.getResources().getInteger(R.integer.buttonDelayMillis);
+
+ viewBinding.trackControllerRecord.setOnTouchListener(this);
+ viewBinding.trackControllerRecord.setOnClickListener((view) -> {
+ if (buttonDelay != null || (isRecording && !isPaused)) {
+ return;
+ }
+
+ callback.recordStart();
+ });
+
+ viewBinding.trackControllerStop.setOnTouchListener(this);
+ transition = (TransitionDrawable) viewBinding.trackControllerContainer.getBackground();
}
- 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, viewBinding.trackControllerRecord, 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) {
- viewBinding.trackControllerRecord.setImageResource(R.drawable.ic_button_pause);
- if (buttonDelay != null) {
- buttonDelay.canceled = true;
- }
- return true;
- }
- } else if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
- recordListener.onClick(null);
+ @SuppressLint("ClickableViewAccessibility") //We do accessibility manually.
+ @Override
+ public boolean onTouch(final View view, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ return false;
+ }
+
+ if (buttonDelay != null && event.getAction() == MotionEvent.ACTION_UP) {
+ handlerUpdateTotalTime.removeCallbacks(buttonDelay);
+ buttonDelay = null;
+ ((ImageButton) view).setPressed(false);
+
+ transition.resetTransition();
+
+ showStatusSetDefaultText();
+
+ return false;
+ }
+
+ //Trigger button pressed animation
+ ((ImageButton) view).setPressed(true);
+
+ //To pause a recording
+ if (viewBinding.trackControllerRecord.equals(view)
+ && event.getAction() == MotionEvent.ACTION_DOWN
+ && isRecording && !isPaused) {
+
+ transition.startTransition(buttonDelayDuration);
+
+ ActivityUtils.vibrate(activity, 150);
+ viewBinding.trackControllerStatus.setText(R.string.hold_to_pause);
+
+ buttonDelay = () -> {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ view.performClick();
+ callback.recordStart(); //TODO Should be recordPause();
+
+ transition.resetTransition();
+
+ ActivityUtils.vibrate(activity, 1000);
+ };
+ handlerUpdateTotalTime.postDelayed(buttonDelay, buttonDelayDuration);
return true;
}
+
+ //To stop a recording
+ if (viewBinding.trackControllerStop.equals(view)
+ && event.getAction() == MotionEvent.ACTION_DOWN
+ && isRecording) {
+
+
+ transition.startTransition(buttonDelayDuration);
+
+ ActivityUtils.vibrate(activity, 150);
+ viewBinding.trackControllerStatus.setText(R.string.hold_to_stop);
+
+ buttonDelay = () -> {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ callback.recordStop();
+ ActivityUtils.vibrate(activity, 1000);
+
+ transition.resetTransition();
+ };
+ handlerUpdateTotalTime.postDelayed(buttonDelay, buttonDelayDuration);
+ 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, viewBinding.trackControllerStop, 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) {
- viewBinding.trackControllerStop.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 Drawable drawable;
- 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.drawable = ContextCompat.getDrawable(activity, animDrawableId);
- this.delayMessageId = delayMessageId;
- }
-
- @Override
- public void run() {
- activity.runOnUiThread(() -> {
- imageButton.setImageDrawable(drawable);
- if (drawable instanceof AnimatedVectorDrawable) {
- ((AnimatedVectorDrawable) drawable).start();
- }
- ActivityUtils.vibrate(activity, 150);
- 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) {
if (!isResumed) {
return;
@@ -182,13 +180,12 @@ public class TrackController {
viewBinding.trackControllerRecord.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
viewBinding.trackControllerRecord.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
- viewBinding.trackControllerStop.setImageResource(isRecording ? R.drawable.ic_button_stop : R.drawable.ic_button_stop_disabled);
viewBinding.trackControllerStop.setEnabled(isRecording);
viewBinding.trackControllerStatus.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
if (isRecording) {
viewBinding.trackControllerStatus.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
- viewBinding.trackControllerStatus.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
+ showStatusSetDefaultText();
}
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
@@ -228,4 +225,14 @@ public class TrackController {
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
}
+
+ private void showStatusSetDefaultText() {
+ viewBinding.trackControllerStatus.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
+ }
+
+ public interface Callback {
+ void recordStart();
+
+ void recordStop();
+ }
}
diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
index 349b7f091..ed3fe4db1 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
@@ -33,7 +33,6 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -65,7 +64,7 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
*
* @author Leif Hendrik Wilden
*/
-public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
+public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback {
private static final String TAG = TrackListActivity.class.getSimpleName();
@@ -133,14 +132,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private MenuItem searchMenuItem;
private MenuItem startGpsMenuItem;
- private final OnClickListener stopListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- updateMenuItems(false, false);
- trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
- }
- };
-
// Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() {
@Override
@@ -175,27 +166,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
}
};
- private final OnClickListener recordListener = new OnClickListener() {
- public void onClick(View v) {
- if (!PreferencesUtils.isRecording(recordingTrackId)) {
- // Not recording -> Recording
- updateMenuItems(false, true);
- Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
- startActivity(newIntent);
- } else if (recordingTrackPaused) {
- // Paused -> Resume
- updateMenuItems(false, true);
- trackRecordingServiceConnection.resumeTrack();
- trackController.update(true, false);
- } else {
- // Recording -> Paused
- updateMenuItems(false, true);
- trackRecordingServiceConnection.pauseTrack();
- trackController.update(true, true);
- }
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
// Reset theme after splash
@@ -211,7 +181,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
- trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, true, recordListener, stopListener);
+ trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, true, this);
// Show trackController when search dialog is dismissed
@@ -577,4 +547,30 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
resourceCursorAdapter.swapCursor(null);
}
}
+
+ @Override
+ public void recordStart() {
+ if (!PreferencesUtils.isRecording(recordingTrackId)) {
+ // Not recording -> Recording
+ updateMenuItems(false, true);
+ Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
+ startActivity(newIntent);
+ } else if (recordingTrackPaused) {
+ // Paused -> Resume
+ updateMenuItems(false, true);
+ trackRecordingServiceConnection.resumeTrack();
+ trackController.update(true, false);
+ } else {
+ // Recording -> Paused
+ updateMenuItems(false, true);
+ trackRecordingServiceConnection.pauseTrack();
+ trackController.update(true, true);
+ }
+ }
+
+ @Override
+ public void recordStop() {
+ updateMenuItems(false, false);
+ trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
+ }
}
diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
index a19f543d0..079b790f2 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
@@ -9,7 +9,6 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.WindowManager;
import androidx.annotation.NonNull;
@@ -42,7 +41,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
*/
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
//This mode of communication should be removed anyhow.
-public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface {
+public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -126,36 +125,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private MenuItem insertMarkerMenuItem;
private MenuItem markerListMenuItem;
- private final OnClickListener recordListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (recordingTrackPaused) {
- // Paused -> Resume
- updateMenuItems(false);
- trackRecordingServiceConnection.resumeTrack();
- trackController.update(true, false);
- } else {
- // Recording -> Paused
- updateMenuItems(true);
- trackRecordingServiceConnection.pauseTrack();
- trackController.update(true, true);
- }
- }
- };
-
- private final OnClickListener stopListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
- Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
- .putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
- startActivity(newIntent);
- overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
- updateMenuItems(true);
- finish();
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -185,7 +154,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
}
- trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, false, recordListener, stopListener);
+ trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, false, this);
}
@Override
@@ -363,6 +332,32 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
}
+ @Override
+ public void recordStart() {
+ if (recordingTrackPaused) {
+ // Paused -> Resume
+ updateMenuItems(false);
+ trackRecordingServiceConnection.resumeTrack();
+ trackController.update(true, false);
+ } else {
+ // Recording -> Paused
+ updateMenuItems(true);
+ trackRecordingServiceConnection.pauseTrack();
+ trackController.update(true, true);
+ }
+ }
+
+ @Override
+ public void recordStop() {
+ trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
+ Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
+ .putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
+ startActivity(newIntent);
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ updateMenuItems(true);
+ finish();
+ }
+
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
diff --git a/src/main/res/animator/button_animator.xml b/src/main/res/animator/button_animator.xml
deleted file mode 100644
index 2f3e87783..000000000
--- a/src/main/res/animator/button_animator.xml
+++ /dev/null
@@ -1,5 +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
new file mode 100644
index 000000000..ce380562e
--- /dev/null
+++ b/src/main/res/drawable/button_stop.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ 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 675e69873..1a3b62da5 100644
--- a/src/main/res/drawable/ic_button_pause.xml
+++ b/src/main/res/drawable/ic_button_pause.xml
@@ -24,9 +24,4 @@
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
deleted file mode 100644
index 16851e029..000000000
--- a/src/main/res/drawable/ic_button_pause_anim.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/src/main/res/drawable/ic_button_stop_anim.xml b/src/main/res/drawable/ic_button_stop_anim.xml
deleted file mode 100644
index 22e792815..000000000
--- a/src/main/res/drawable/ic_button_stop_anim.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/drawable/track_controller_background.xml b/src/main/res/drawable/track_controller_background.xml
new file mode 100644
index 000000000..c6074c9ec
--- /dev/null
+++ b/src/main/res/drawable/track_controller_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/layout-land/track_controller.xml b/src/main/res/layout-land/track_controller.xml
index e4ba7f618..29df62576 100644
--- a/src/main/res/layout-land/track_controller.xml
+++ b/src/main/res/layout-land/track_controller.xml
@@ -19,7 +19,7 @@ limitations under the License.
android:id="@+id/track_controller_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:background="@color/track_controller_background"
+ android:background="@color/track_controller_background_normal"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp">
@@ -30,7 +30,8 @@ limitations under the License.
android:layout_height="0dp"
android:layout_weight="10"
android:background="@android:color/transparent"
- android:contentDescription="@string/image_record" />
+ android:contentDescription="@string/image_record"
+ android:src="@drawable/ic_button_record" />
+ android:src="@drawable/button_stop" />
\ No newline at end of file
diff --git a/src/main/res/layout/track_controller.xml b/src/main/res/layout/track_controller.xml
index 617545ecf..716c39475 100644
--- a/src/main/res/layout/track_controller.xml
+++ b/src/main/res/layout/track_controller.xml
@@ -13,13 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+
@@ -29,32 +30,36 @@ limitations under the License.
android:layout_height="wrap_content"
android:layout_weight="10"
android:background="@android:color/transparent"
- android:contentDescription="@string/image_record" />
+ android:contentDescription="@string/image_record"
+ android:src="@drawable/ic_button_record" />
+ android:layout_gravity="center"
+ android:layout_weight="6">
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ tools:text="0:12:34" />
+ android:gravity="center"
+ android:singleLine="true"
+ tools:text="@string/generic_recording" />
+ android:src="@drawable/button_stop" />
\ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index e3403afa0..7ce31f0d3 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -35,7 +35,8 @@ limitations under the License.
#B2000000
#00000000
#FF000000
- #FF0E0E0E
+ #FF0E0E0E
+ #3F0000
#272727
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index df7e807b4..268f80b6b 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -631,8 +631,8 @@ 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
+ Hold to stop
+ Hold to pause
Last track exported
Export track to storage after recording is finished