Added last song play in sleep timer

This commit is contained in:
h4h13 2019-05-16 00:15:42 +05:30
parent 2837147d36
commit b94e94a636
10 changed files with 217 additions and 119 deletions

View file

@ -43,6 +43,8 @@ object Constants {
@JvmField @JvmField
val ACTION_QUIT = "$RETRO_MUSIC_PACKAGE_NAME.quitservice" val ACTION_QUIT = "$RETRO_MUSIC_PACKAGE_NAME.quitservice"
@JvmField @JvmField
val ACTION_PENDING_QUIT = "$RETRO_MUSIC_PACKAGE_NAME.pendingquitservice"
@JvmField
val INTENT_EXTRA_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist" val INTENT_EXTRA_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist"
@JvmField @JvmField
val INTENT_EXTRA_SHUFFLE_MODE = "$RETRO_MUSIC_PACKAGE_NAME.intentextra.shufflemode" val INTENT_EXTRA_SHUFFLE_MODE = "$RETRO_MUSIC_PACKAGE_NAME.intentextra.shufflemode"

View file

@ -24,6 +24,7 @@ import androidx.core.app.ActivityCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.list.listItems import com.afollestad.materialdialogs.list.listItems
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -95,9 +96,9 @@ class BlacklistFolderChooserDialog : DialogFragment() {
checkIfCanGoUp() checkIfCanGoUp()
parentContents = listFiles() parentContents = listFiles()
return MaterialDialog(activity!!).show { return MaterialDialog(activity!!, BottomSheet()).show {
title(text = parentFolder!!.absolutePath) title(text = parentFolder!!.absolutePath)
listItems(items = contentsArray(), waitForPositiveButton = false) { dialog, index, text -> listItems(items = contentsArray(), waitForPositiveButton = false) { _, index, _ ->
onSelection(index) onSelection(index)
} }
noAutoDismiss() noAutoDismiss()
@ -138,7 +139,7 @@ class BlacklistFolderChooserDialog : DialogFragment() {
dialog?.apply { dialog?.apply {
setTitle(parentFolder!!.absolutePath) setTitle(parentFolder!!.absolutePath)
listItems(items = contentsArray()) { dialog, index, text -> listItems(items = contentsArray()) { _, index, _ ->
onSelection(index) onSelection(index)
} }
} }

View file

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.dialogs package code.name.monkey.retromusic.dialogs
import android.app.AlarmManager import android.app.AlarmManager
import android.app.Dialog
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
@ -22,50 +23,172 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.os.SystemClock import android.os.SystemClock
import android.view.LayoutInflater import android.widget.CheckBox
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.fragment.app.DialogFragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.Constants.ACTION_QUIT
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.util.ViewUtil
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment import com.afollestad.materialdialogs.MaterialDialog
import kotlinx.android.synthetic.main.dialog_sleep_timer.* import com.afollestad.materialdialogs.WhichButton
import java.util.* import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.callbacks.onShow
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
class SleepTimerDialog : RoundedBottomSheetDialogFragment() {
class SleepTimerDialog : DialogFragment() {
private var seekArcProgress: Int = 0 private var seekArcProgress: Int = 0
private lateinit var timerUpdater: TimerUpdater private lateinit var timerUpdater: TimerUpdater
private lateinit var materialDialog: MaterialDialog
private lateinit var shouldFinishLastSong: CheckBox
private lateinit var seekBar: SeekBar
private lateinit var timerDisplay: TextView
override fun onDismiss(dialog: DialogInterface) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onDismiss(dialog) timerUpdater = TimerUpdater()
timerUpdater.cancel()
materialDialog = MaterialDialog(activity!!, BottomSheet())
.title(R.string.action_sleep_timer)
.positiveButton(R.string.action_set) {
PreferenceUtil.getInstance().sleepTimerFinishMusic = shouldFinishLastSong.isChecked
val minutes = seekArcProgress
val pi = makeTimerPendingIntent(PendingIntent.FLAG_CANCEL_CURRENT)
val nextSleepTimerElapsedTime = SystemClock.elapsedRealtime() + minutes * 60 * 1000
PreferenceUtil.getInstance().setNextSleepTimerElapsedRealtime(nextSleepTimerElapsedTime)
val am = activity!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSleepTimerElapsedTime, pi)
Toast.makeText(activity, activity!!.resources.getString(R.string.sleep_timer_set, minutes), Toast.LENGTH_SHORT).show()
}
.negativeButton(android.R.string.cancel) {
if (activity == null) {
return@negativeButton
}
val previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE)
if (previous != null) {
val am = activity!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.cancel(previous)
previous.cancel()
Toast.makeText(activity, activity!!.resources.getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show()
}
val musicService = MusicPlayerRemote.musicService
if (musicService != null && musicService.pendingQuit) {
musicService.pendingQuit = false
Toast.makeText(activity, activity!!.resources.getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show()
}
}
.customView(R.layout.dialog_sleep_timer, scrollable = false)
.show {
onShow {
if (makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE) != null) {
timerUpdater.start()
}
}
}
if (activity == null || materialDialog.getCustomView() == null) {
return materialDialog
}
shouldFinishLastSong = materialDialog.getCustomView().findViewById(R.id.shouldFinishLastSong)
seekBar = materialDialog.getCustomView().findViewById(R.id.seekBar)
timerDisplay = materialDialog.getCustomView().findViewById(R.id.timerDisplay)
val finishMusic = PreferenceUtil.getInstance().sleepTimerFinishMusic
shouldFinishLastSong.isChecked = finishMusic
seekArcProgress = PreferenceUtil.getInstance().lastSleepTimerValue
updateTimeDisplayTime()
seekBar.progress = seekArcProgress
setProgressBarColor(ThemeStore.accentColor(context!!))
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
if (i < 1) {
seekBar.progress = 1
return
}
seekArcProgress = i
updateTimeDisplayTime()
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
PreferenceUtil.getInstance().lastSleepTimerValue = seekArcProgress
}
})
return materialDialog
} }
override fun onResume() { private fun updateTimeDisplayTime() {
super.onResume() timerDisplay.text = "$seekArcProgress min"
if (makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE) != null) { }
timerUpdater.start()
private fun makeTimerPendingIntent(flag: Int): PendingIntent {
return PendingIntent.getService(activity, 0, makeTimerIntent(), flag)
}
private fun makeTimerIntent(): Intent {
val intent = Intent(activity, MusicService::class.java)
return if (shouldFinishLastSong.isChecked) {
intent.setAction(Constants.ACTION_PENDING_QUIT)
} else intent.setAction(Constants.ACTION_QUIT)
}
private fun updateCancelButton() {
val musicService = MusicPlayerRemote.musicService
if (musicService != null && musicService.pendingQuit) {
materialDialog.getActionButton(WhichButton.NEGATIVE).text = materialDialog.context.getString(R.string.cancel_current_timer)
} else {
materialDialog.getActionButton(WhichButton.NEGATIVE).text = null
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { private inner class TimerUpdater internal constructor() : CountDownTimer(PreferenceUtil.getInstance().nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(), 1000) {
return inflater.inflate(R.layout.dialog_sleep_timer, container, false)
override fun onTick(millisUntilFinished: Long) {
materialDialog.getActionButton(WhichButton.NEGATIVE).text =
String.format("%s %s", materialDialog.context.getString(R.string.cancel_current_timer),
" (" + MusicUtil.getReadableDurationString(millisUntilFinished) + ")")
}
override fun onFinish() {
updateCancelButton()
}
} }
/* override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_sleep_timer, container, false)
}*/
private fun setProgressBarColor(dark: Int) { private fun setProgressBarColor(dark: Int) {
ViewUtil.setProgressDrawable(progressSlider = seekBar, newColor = dark) ViewUtil.setProgressDrawable(progressSlider = seekBar, newColor = dark)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { /*override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
MaterialUtil.setTint(actionCancel, false) MaterialUtil.setTint(actionCancel, false)
@ -127,30 +250,7 @@ class SleepTimerDialog : RoundedBottomSheetDialogFragment() {
dismiss() dismiss()
} }
} }
} }*/
private fun updateTimeDisplayTime() {
timerDisplay!!.text = String.format(Locale.getDefault(), "%d min", seekArcProgress)
}
private fun makeTimerPendingIntent(flag: Int): PendingIntent? {
return PendingIntent.getService(activity, 0, makeTimerIntent(), flag)
}
private fun makeTimerIntent(): Intent {
return Intent(activity, MusicService::class.java)
.setAction(ACTION_QUIT)
}
private inner class TimerUpdater internal constructor() : CountDownTimer(PreferenceUtil.getInstance().nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(), 1000) {
override fun onTick(millisUntilFinished: Long) {
actionCancel.text = String.format("%s (%s)", getString(R.string.cancel_current_timer), MusicUtil.getReadableDurationString(millisUntilFinished))
}
override fun onFinish() {
actionCancel.text = null
actionCancel.visibility = View.GONE
}
}
} }

View file

@ -38,7 +38,7 @@ import java.util.*
object MusicPlayerRemote { object MusicPlayerRemote {
val TAG = MusicPlayerRemote::class.java.simpleName val TAG: String = MusicPlayerRemote::class.java.simpleName
private val mConnectionMap = WeakHashMap<Context, ServiceBinder>() private val mConnectionMap = WeakHashMap<Context, ServiceBinder>()
var musicService: MusicService? = null var musicService: MusicService? = null

View file

@ -49,6 +49,9 @@ import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -56,8 +59,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig; import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard; import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
@ -87,6 +88,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroUtil; import code.name.monkey.retromusic.util.RetroUtil;
import static code.name.monkey.retromusic.Constants.ACTION_PAUSE; import static code.name.monkey.retromusic.Constants.ACTION_PAUSE;
import static code.name.monkey.retromusic.Constants.ACTION_PENDING_QUIT;
import static code.name.monkey.retromusic.Constants.ACTION_PLAY; import static code.name.monkey.retromusic.Constants.ACTION_PLAY;
import static code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST; import static code.name.monkey.retromusic.Constants.ACTION_PLAY_PLAYLIST;
import static code.name.monkey.retromusic.Constants.ACTION_QUIT; import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
@ -176,6 +178,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
}; };
public boolean pendingQuit = false;
private Playback playback; private Playback playback;
private ArrayList<Song> playingQueue = new ArrayList<>(); private ArrayList<Song> playingQueue = new ArrayList<>();
private ArrayList<Song> originalPlayingQueue = new ArrayList<>(); private ArrayList<Song> originalPlayingQueue = new ArrayList<>();
@ -448,7 +451,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
break; break;
case ACTION_STOP: case ACTION_STOP:
case ACTION_QUIT: case ACTION_QUIT:
return quit(); pendingQuit = false;
quit();
break;
case ACTION_PENDING_QUIT:
pendingQuit = true;
break;
} }
} }
} }
@ -1178,6 +1186,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
return playback.getAudioSessionId(); return playback.getAudioSessionId();
} }
@NonNull
public MediaSessionCompat getMediaSession() { public MediaSessionCompat getMediaSession() {
return mediaSession; return mediaSession;
} }
@ -1193,7 +1202,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
} }
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, @NonNull String key) {
switch (key) { switch (key) {
case PreferenceUtil.GAPLESS_PLAYBACK: case PreferenceUtil.GAPLESS_PLAYBACK:
if (sharedPreferences.getBoolean(key, false)) { if (sharedPreferences.getBoolean(key, false)) {
@ -1251,10 +1260,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
@Override @Override
public void handleMessage(@NonNull Message msg) { public void handleMessage(@NonNull Message msg) {
final MusicService service = mService.get(); final MusicService service = mService.get();
switch (msg.what) { if (msg.what == SAVE_QUEUES) {
case SAVE_QUEUES: service.saveQueuesImpl();
service.saveQueuesImpl();
break;
} }
} }
} }
@ -1317,9 +1324,16 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
break; break;
case TRACK_ENDED: case TRACK_ENDED:
if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { // if there is a timer finished, don't continue
if (service.pendingQuit ||
service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.notifyChange(PLAY_STATE_CHANGED); service.notifyChange(PLAY_STATE_CHANGED);
service.seek(0); service.seek(0);
if (service.pendingQuit) {
service.pendingQuit = false;
service.quit();
break;
}
} else { } else {
service.playNextSong(false); service.playNextSong(false);
} }

View file

@ -57,6 +57,7 @@ public final class PreferenceUtil {
public static final String GAPLESS_PLAYBACK = "gapless_playback"; public static final String GAPLESS_PLAYBACK = "gapless_playback";
public static final String ALBUM_ART_ON_LOCKSCREEN = "album_art_on_lockscreen"; public static final String ALBUM_ART_ON_LOCKSCREEN = "album_art_on_lockscreen";
public static final String BLURRED_ALBUM_ART = "blurred_album_art"; public static final String BLURRED_ALBUM_ART = "blurred_album_art";
public static final String SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song";
public static final String TOGGLE_HEADSET = "toggle_headset"; public static final String TOGGLE_HEADSET = "toggle_headset";
public static final String DOMINANT_COLOR = "dominant_color"; public static final String DOMINANT_COLOR = "dominant_color";
public static final String GENERAL_THEME = "general_theme"; public static final String GENERAL_THEME = "general_theme";
@ -156,6 +157,17 @@ public final class PreferenceUtil {
} }
} }
public void setSleepTimerFinishMusic(final boolean value) {
final SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean(SLEEP_TIMER_FINISH_SONG, value);
editor.apply();
}
public boolean getSleepTimerFinishMusic() {
return mPreferences.getBoolean(SLEEP_TIMER_FINISH_SONG, false);
}
public String getUserBio() { public String getUserBio() {
return mPreferences.getString(USER_BIO, ""); return mPreferences.getString(USER_BIO, "");
} }

View file

@ -1,71 +1,35 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="16dp"> android:orientation="vertical">
<LinearLayout <code.name.monkey.appthemehelper.common.views.ATESecondaryTextView
android:id="@+id/timerDisplay"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:maxHeight="3dp"
android:paddingTop="12dp"
android:progressDrawable="@drawable/color_progress_seek"
android:splitTrack="false"
android:thumb="@drawable/switch_thumb_material"
tools:progress="20" />
<code.name.monkey.appthemehelper.common.views.ATEPrimaryTextView <CheckBox
android:id="@+id/title" android:id="@+id/shouldFinishLastSong"
style="@style/TextAppearance.MaterialComponents.Headline6" style="@style/TextAppearance.MaterialComponents.Subtitle1"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:layout_gravity="center"
android:text="@string/action_sleep_timer" /> android:text="@string/finish_last_song" />
<code.name.monkey.appthemehelper.common.views.ATESecondaryTextView </LinearLayout>
android:id="@+id/timerDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
android:textAppearance="?android:textAppearanceLarge" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="3dp"
android:paddingTop="12dp"
android:progressDrawable="@drawable/color_progress_seek"
android:splitTrack="false"
android:thumb="@drawable/switch_thumb_material"
tools:progress="20" />
<com.google.android.material.button.MaterialButton
android:id="@+id/actionSet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:gravity="center_vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/action_set"
android:textColor="@color/md_white_1000"
app:backgroundTint="@color/md_pink_A400" />
<com.google.android.material.button.MaterialButton
android:id="@+id/actionCancel"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:gravity="center_vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@android:string/cancel"
app:strokeWidth="2dp"
tools:visibility="visible" />
</LinearLayout>
</FrameLayout>

View file

@ -45,6 +45,9 @@
<TextView <TextView
android:id="@+id/bannerTitle" android:id="@+id/bannerTitle"
style="@style/BigTitleTextAppearanceToolbar" style="@style/BigTitleTextAppearanceToolbar"
android:layout_gravity="start|center_vertical"
android:paddingStart="14dp"
android:paddingEnd="8dp"
android:text="@string/folders" android:text="@string/folders"
tools:ignore="MissingPrefix" /> tools:ignore="MissingPrefix" />

View file

@ -54,6 +54,7 @@
style="@style/BigTitleTextAppearanceToolbar" style="@style/BigTitleTextAppearanceToolbar"
android:layout_gravity="start|center_vertical" android:layout_gravity="start|center_vertical"
android:paddingStart="14dp" android:paddingStart="14dp"
android:paddingEnd="8dp"
android:text="@string/library" android:text="@string/library"
android:textColor="@color/md_white_1000" android:textColor="@color/md_white_1000"
tools:ignore="MissingPrefix" /> tools:ignore="MissingPrefix" />

View file

@ -615,5 +615,6 @@
<string name="select_preset">Select preset</string> <string name="select_preset">Select preset</string>
<string name="upgrade_to_premium">Upgrade to premium</string> <string name="upgrade_to_premium">Upgrade to premium</string>
<string name="action_new_playlist">New playlist</string> <string name="action_new_playlist">New playlist</string>
<string name="finish_last_song">Finish last song</string>
</resources> </resources>