Add a Jump to Time dialog (#868)
* Add a Jump to Time dialog This implements #827 by adding a context menu entry to open a dialog that allows the user to input a time/position to seek to for the current song. * Use callback interface for JumpToTimeDialog position submission * Automatically show soft keyboard for JumpToTimeDialog * Automatically move to next EditText in JumpToTimeDialog * Crash if JumpToTimeDialog is started by wrong activity
This commit is contained in:
parent
c112925890
commit
d00b3e4769
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -430,6 +430,7 @@ public abstract class PlaybackActivity extends Activity
|
|||||||
static final int MENU_MORE_ARTIST = 23;
|
static final int MENU_MORE_ARTIST = 23;
|
||||||
static final int MENU_MORE_GENRE = 24;
|
static final int MENU_MORE_GENRE = 24;
|
||||||
static final int MENU_MORE_FOLDER = 25;
|
static final int MENU_MORE_FOLDER = 25;
|
||||||
|
static final int MENU_JUMP_TO_TIME = 26;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu)
|
public boolean onCreateOptionsMenu(Menu menu)
|
||||||
|
@ -1737,7 +1737,19 @@ public final class PlaybackService extends Service
|
|||||||
if (!mMediaPlayerInitialized)
|
if (!mMediaPlayerInitialized)
|
||||||
return;
|
return;
|
||||||
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
|
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);
|
mHandler.sendEmptyMessage(MSG_BROADCAST_SEEK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,8 @@ import java.net.InetAddress;
|
|||||||
public class SlidingPlaybackActivity extends PlaybackActivity
|
public class SlidingPlaybackActivity extends PlaybackActivity
|
||||||
implements SlidingView.Callback,
|
implements SlidingView.Callback,
|
||||||
SeekBar.OnSeekBarChangeListener,
|
SeekBar.OnSeekBarChangeListener,
|
||||||
PlaylistDialog.Callback
|
PlaylistDialog.Callback,
|
||||||
|
JumpToTimeDialog.OnPositionSubmitListener
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Reference to the inflated menu
|
* 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_CLEAR_QUEUE, 20, R.string.dequeue_rest);
|
||||||
menu.add(0, MENU_EMPTY_QUEUE, 20, R.string.empty_the_queue);
|
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_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.
|
// This should only be required on ICS.
|
||||||
onSlideExpansionChanged(SlidingView.EXPANSION_PARTIAL);
|
onSlideExpansionChanged(SlidingView.EXPANSION_PARTIAL);
|
||||||
return true;
|
return true;
|
||||||
@ -147,6 +149,9 @@ public class SlidingPlaybackActivity extends PlaybackActivity
|
|||||||
PlaylistDialog dialog = PlaylistDialog.newInstance(this, null, null);
|
PlaylistDialog dialog = PlaylistDialog.newInstance(this, null, null);
|
||||||
dialog.show(getFragmentManager(), "PlaylistDialog");
|
dialog.show(getFragmentManager(), "PlaylistDialog");
|
||||||
break;
|
break;
|
||||||
|
case MENU_JUMP_TO_TIME:
|
||||||
|
JumpToTimeDialog.show(getFragmentManager());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
59
app/src/main/res/layout/duration_input.xml
Normal file
59
app/src/main/res/layout/duration_input.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/hours"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="2"
|
||||||
|
android:hint="@string/hour_hint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/time_separator"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/minutes"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="2"
|
||||||
|
android:hint="@string/minute_hint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/time_separator"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/seconds"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="2"
|
||||||
|
android:hint="@string/second_hint" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -329,6 +329,13 @@ THE SOFTWARE.
|
|||||||
<string name="preferences_action_enqueue_current_artist">Enqueue artist</string>
|
<string name="preferences_action_enqueue_current_artist">Enqueue artist</string>
|
||||||
<string name="preferences_action_enqueue_current_genre">Enqueue genre</string>
|
<string name="preferences_action_enqueue_current_genre">Enqueue genre</string>
|
||||||
|
|
||||||
|
<string name="jump_to_time">Jump to Time</string>
|
||||||
|
<string name="time_separator">:</string>
|
||||||
|
<string name="hour_hint">HH</string>
|
||||||
|
<string name="minute_hint">MM</string>
|
||||||
|
<string name="second_hint">SS</string>
|
||||||
|
<string name="error_invalid_position">Invalid position</string>
|
||||||
|
|
||||||
<string name="filebrowser_start">Filebrowser home</string>
|
<string name="filebrowser_start">Filebrowser home</string>
|
||||||
<string name="customize_filebrowser_start">Filebrowser starts at this directory</string>
|
<string name="customize_filebrowser_start">Filebrowser starts at this directory</string>
|
||||||
<string name="select">Select</string>
|
<string name="select">Select</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user