diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/JumpToTimeDialog.java b/app/src/main/java/ch/blinkenlights/android/vanilla/JumpToTimeDialog.java
new file mode 100644
index 00000000..2d77b7ed
--- /dev/null
+++ b/app/src/main/java/ch/blinkenlights/android/vanilla/JumpToTimeDialog.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 Toby Hsieh
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ch.blinkenlights.android.vanilla;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * A dialog for the user to input a specific time to jump to for the current song
+ */
+public class JumpToTimeDialog extends DialogFragment implements DialogInterface.OnClickListener {
+ private EditText hoursView;
+ private EditText minutesView;
+ private EditText secondsView;
+
+ /**
+ * Callback interface for an activity that shows JumpToTimeDialog
+ */
+ public interface OnPositionSubmitListener {
+ /**
+ * Called when the user submits a position to jump/seek to for the current song.
+ *
+ * @param position position to seek/jump to in milliseconds
+ */
+ void onPositionSubmit(int position);
+ }
+
+ /**
+ * Creates and shows the dialog
+ *
+ * @param manager the FragmentManager to add the newly created dialog to
+ */
+ public static void show(FragmentManager manager) {
+ new JumpToTimeDialog().show(manager, "JumpToTimeDialog");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Watcher that moves to the next EditText when 2 digits are inserted
+ TextWatcher textWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ EditText editText = (EditText) getDialog().getCurrentFocus();
+ if (editText.length() == 2) {
+ View view = editText.focusSearch(View.FOCUS_RIGHT);
+ if (view != null) {
+ view.requestFocus();
+ }
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ };
+
+ View view = LayoutInflater.from(getActivity()).inflate(R.layout.duration_input, null);
+ hoursView = view.findViewById(R.id.hours);
+ hoursView.addTextChangedListener(textWatcher);
+ minutesView = view.findViewById(R.id.minutes);
+ minutesView.addTextChangedListener(textWatcher);
+ secondsView = view.findViewById(R.id.seconds);
+ secondsView.addTextChangedListener(textWatcher);
+
+ Dialog dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.jump_to_time)
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ hoursView.requestFocus();
+ dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ return dialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Activity activity = getActivity();
+ try {
+ int hours = parseInteger(hoursView.getText().toString());
+ int minutes = parseInteger(minutesView.getText().toString());
+ int seconds = parseInteger(secondsView.getText().toString());
+ int position = (hours * 3600 + minutes * 60 + seconds) * 1000;
+ ((OnPositionSubmitListener) activity).onPositionSubmit(position);
+ } catch (NumberFormatException e) {
+ Toast.makeText(activity, R.string.error_invalid_position, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ /**
+ * Parses the given string as an integer. This returns 0 if the given string is empty.
+ *
+ * @param s the string to parse
+ * @return the integer result
+ */
+ static int parseInteger(String s) {
+ if (s.length() == 0) {
+ return 0;
+ }
+ return Integer.parseInt(s);
+ }
+}
diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java
index 923b8357..1b9e2ff7 100644
--- a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java
+++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java
@@ -430,6 +430,7 @@ public abstract class PlaybackActivity extends Activity
static final int MENU_MORE_ARTIST = 23;
static final int MENU_MORE_GENRE = 24;
static final int MENU_MORE_FOLDER = 25;
+ static final int MENU_JUMP_TO_TIME = 26;
@Override
public boolean onCreateOptionsMenu(Menu menu)
diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java
index 03faa1fb..d1c48484 100644
--- a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java
+++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java
@@ -1737,7 +1737,19 @@ public final class PlaybackService extends Service
if (!mMediaPlayerInitialized)
return;
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
- mMediaPlayer.seekTo((int)position);
+ seekToPosition((int) position);
+ }
+
+ /**
+ * Seeks to the given position in the current song.
+ *
+ * @param msec the offset in milliseconds from the start to seek to
+ */
+ public void seekToPosition(int msec) {
+ if (!mMediaPlayerInitialized) {
+ return;
+ }
+ mMediaPlayer.seekTo(msec);
mHandler.sendEmptyMessage(MSG_BROADCAST_SEEK);
}
diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java b/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java
index 40748733..395e9469 100644
--- a/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java
+++ b/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java
@@ -34,7 +34,8 @@ import java.net.InetAddress;
public class SlidingPlaybackActivity extends PlaybackActivity
implements SlidingView.Callback,
SeekBar.OnSeekBarChangeListener,
- PlaylistDialog.Callback
+ PlaylistDialog.Callback,
+ JumpToTimeDialog.OnPositionSubmitListener
{
/**
* Reference to the inflated menu
@@ -127,6 +128,7 @@ public class SlidingPlaybackActivity extends PlaybackActivity
menu.add(0, MENU_CLEAR_QUEUE, 20, R.string.dequeue_rest);
menu.add(0, MENU_EMPTY_QUEUE, 20, R.string.empty_the_queue);
menu.add(0, MENU_SAVE_QUEUE, 20, R.string.save_as_playlist);
+ menu.add(0, MENU_JUMP_TO_TIME, 20, R.string.jump_to_time);
// This should only be required on ICS.
onSlideExpansionChanged(SlidingView.EXPANSION_PARTIAL);
return true;
@@ -147,6 +149,9 @@ public class SlidingPlaybackActivity extends PlaybackActivity
PlaylistDialog dialog = PlaylistDialog.newInstance(this, null, null);
dialog.show(getFragmentManager(), "PlaylistDialog");
break;
+ case MENU_JUMP_TO_TIME:
+ JumpToTimeDialog.show(getFragmentManager());
+ break;
default:
return super.onOptionsItemSelected(item);
}
@@ -336,4 +341,9 @@ public class SlidingPlaybackActivity extends PlaybackActivity
}
}
+ @Override
+ public void onPositionSubmit(int position) {
+ PlaybackService.get(this).seekToPosition(position);
+ updateElapsedTime();
+ }
}
diff --git a/app/src/main/res/layout/duration_input.xml b/app/src/main/res/layout/duration_input.xml
new file mode 100644
index 00000000..d807a829
--- /dev/null
+++ b/app/src/main/res/layout/duration_input.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/translatable.xml b/app/src/main/res/values/translatable.xml
index a001fe73..f38a2b77 100644
--- a/app/src/main/res/values/translatable.xml
+++ b/app/src/main/res/values/translatable.xml
@@ -329,6 +329,13 @@ THE SOFTWARE.
Enqueue artist
Enqueue genre
+ Jump to Time
+ :
+ HH
+ MM
+ SS
+ Invalid position
+
Filebrowser home
Filebrowser starts at this directory
Select