diff --git a/app/src/main/assets/chat.html b/app/src/main/assets/chat.html
deleted file mode 100644
index 650c9a2..0000000
--- a/app/src/main/assets/chat.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
index 2c5335a..606603c 100644
Binary files a/app/src/main/ic_launcher-web.png and b/app/src/main/ic_launcher-web.png differ
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt b/app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt
index 1b18345..c236c7f 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt
@@ -71,7 +71,7 @@ abstract class BaseNotification(private val notificationChannelId: String,
builder.setSmallIcon(R.drawable.lollipop_logo)
builder.color = -0xf58b01 // same color as Accent. Can't use c.getColor since it's API23+
} else {
- builder.setSmallIcon(R.drawable.normal_logo)
+ builder.setSmallIcon(R.drawable.logo_roundsquare)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt b/app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt
index 118b168..e8c0c79 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt
@@ -8,8 +8,6 @@ import android.util.Log
import androidx.preference.PreferenceManager
import fr.forum_thalie.tsumugi.alarm.RadioAlarm
import fr.forum_thalie.tsumugi.playerstore.PlayerStore
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
class BootBroadcastReceiver : BroadcastReceiver(){
@@ -19,8 +17,6 @@ class BootBroadcastReceiver : BroadcastReceiver(){
preferenceStore = PreferenceManager.getDefaultSharedPreferences(context)
if (arg1.action == Intent.ACTION_BOOT_COMPLETED) {
- WorkerStore.instance.init(context)
- startStreamerMonitor(context) // will actually start it only if enabled in settings
RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
}
@@ -29,8 +25,6 @@ class BootBroadcastReceiver : BroadcastReceiver(){
RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
if (PlayerStore.instance.streamerName.value.isNullOrBlank())
PlayerStore.instance.initPicture(context)
- if (!PlayerStore.instance.isInitialized)
- PlayerStore.instance.initApi()
val i = Intent(context, RadioService::class.java)
i.putExtra("action", Actions.PLAY_OR_FALLBACK.name)
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt b/app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt
index 9e6fc8f..acf7cd6 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt
@@ -15,12 +15,8 @@ import fr.forum_thalie.tsumugi.playerstore.PlayerStore
import java.util.Timer
import android.view.MenuItem
-import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
import fr.forum_thalie.tsumugi.alarm.RadioAlarm
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
/* Log to file import
@@ -39,11 +35,11 @@ class MainActivity : BaseActivity() {
* Called on first creation and when restoring state.
*/
private fun setupBottomNavigationBar() {
- val bottomNavigationView = findViewById(R.id.bottom_nav)
+ val bottomNavigationView : BottomNavigationView = findViewById(R.id.bottom_nav)
//val navGraphIds = listOf(R.navigation.home, R.navigation.list, R.navigation.form)
val navGraphIds = listOf(R.navigation.navigation_nowplaying, R.navigation.navigation_songs,
- R.navigation.navigation_news, R.navigation.navigation_chat)
+ R.navigation.navigation_news)
// Setup the bottom navigation view with a list of navigation graphs
val controller = bottomNavigationView.setupWithNavController(
@@ -77,15 +73,15 @@ class MainActivity : BaseActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle item selection
return when (item.itemId) {
+ /*
R.id.action_refresh -> {
PlayerStore.instance.queue.clear()
PlayerStore.instance.lp.clear()
- PlayerStore.instance.initApi()
- Requestor.instance.initFavorites()
val s = Snackbar.make(findViewById(R.id.nav_host_container), "Refreshing data..." as CharSequence, Snackbar.LENGTH_LONG)
s.show()
true
}
+ */
R.id.action_settings -> {
val i = Intent(this, ParametersActivity::class.java)
startActivity(i)
@@ -118,10 +114,6 @@ class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
- WorkerStore.instance.init(this)
- startStreamerMonitor(this) // this checks the preferenceStore before actually starting a service, don't worry.
-
RadioAlarm.instance.cancelAlarm(c = this)
RadioAlarm.instance.setNextAlarm(c = this) // this checks the preferenceStore before actually setting an alarm, don't worry.
@@ -132,8 +124,6 @@ class MainActivity : BaseActivity() {
colorRedList = (ResourcesCompat.getColorStateList(resources, R.color.button_red, null))
colorGreenListCompat = (ResourcesCompat.getColorStateList(resources, R.color.button_green_compat, null))
- PlayerStore.instance.initApi() // the service will call the initApi on defining the streamerName Observer too, but it's better to initialize the API as soon as the user opens the activity.
-
// Post-UI Launch
if (PlayerStore.instance.isInitialized)
{
@@ -149,9 +139,6 @@ class MainActivity : BaseActivity() {
// initialize some API data
PlayerStore.instance.initPicture(this)
PlayerStore.instance.streamerName.value = "" // initializing the streamer name will trigger an initApi from the observer in the Service.
-
- // initialize the favorites
- Requestor.instance.initFavorites()
}
if (!isTimerStarted)
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt
index 4bcef87..d8294da 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt
@@ -29,7 +29,6 @@ class ParametersActivity : BaseActivity() {
ActionOpenParam.ALARM.name -> AlarmFragment()
ActionOpenParam.SLEEP.name -> SleepFragment()
ActionOpenParam.CUSTOMIZE.name -> CustomizeFragment()
- ActionOpenParam.STREAMER_NOTIFICATION_SERVICE.name -> StreamerNotifServiceFragment()
else -> MainPreferenceFragment()
}
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt b/app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt
index 6ed4eb5..0fada1d 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt
@@ -107,29 +107,6 @@ class RadioService : MediaBrowserServiceCompat() {
// ################### OBSERVERS ####################
// ##################################################
- private val titleObserver: Observer = Observer {
- if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_PLAYING)
- {
- Log.d(tag, radioTag + "SONG CHANGED AND PLAYING")
- // we activate latency compensation only if it's been at least 2 songs...
- when {
- PlayerStore.instance.isStreamDown -> {
- // if we reach here, it means that the observer has been called by a new song and that the stream was down previously.
- // so the stream is now back to normal.
- PlayerStore.instance.isStreamDown = false
- PlayerStore.instance.initApi()
- }
- PlayerStore.instance.currentSong.title.value == getString(R.string.ed) -> {
- PlayerStore.instance.isStreamDown = true
- }
- else -> {
- PlayerStore.instance.fetchApi(numberOfSongs >= 2)
- }
- }
- }
- nowPlayingNotification.update(this)
- }
-
private val volumeObserver: Observer = Observer {
setVolume(it)
}
@@ -145,20 +122,14 @@ class RadioService : MediaBrowserServiceCompat() {
stopPlaying()
}
- private val startTimeObserver = Observer {
- // We're listening to startTime to determine if we have to update Queue and Lp.
- // this is because startTime is set by the API and never by the ICY, so both cases are covered (playing and stopped)
- // should be OK even when a new streamer comes in.
- if (it != PlayerStore.instance.currentSongBackup.startTime.value) // we have a new song
+ private val titleObserver = Observer {
+ // We're checking if a new song arrives. If so, we put the currentSong in Lp and update the backup.
+ if (PlayerStore.instance.currentSong != PlayerStore.instance.currentSongBackup
+ && it != noConnectionValue)
{
PlayerStore.instance.updateLp()
- PlayerStore.instance.updateQueue()
}
- }
-
- private val streamerObserver = Observer {
- PlayerStore.instance.initApi()
- nowPlayingNotification.update(this) // should update the streamer icon
+ nowPlayingNotification.update(this)
}
private val streamerPictureObserver = Observer {
@@ -224,9 +195,7 @@ class RadioService : MediaBrowserServiceCompat() {
nowPlayingNotification.create(this, mediaSession)
- PlayerStore.instance.streamerName.observeForever(streamerObserver)
PlayerStore.instance.currentSong.title.observeForever(titleObserver)
- PlayerStore.instance.currentSong.startTime.observeForever(startTimeObserver)
PlayerStore.instance.volume.observeForever(volumeObserver)
PlayerStore.instance.isPlaying.observeForever(isPlayingObserver)
PlayerStore.instance.isMuted.observeForever(isMutedObserver)
@@ -234,12 +203,6 @@ class RadioService : MediaBrowserServiceCompat() {
startForeground(radioServiceId, nowPlayingNotification.notification)
- // start ticker for when the player is stopped
- val periodString = PreferenceManager.getDefaultSharedPreferences(this).getString("fetchPeriod", "10") ?: "10"
- val period: Long = Integer.parseInt(periodString).toLong()
- if (period > 0)
- apiTicker.schedule(ApiFetchTick(), 0, period * 1000)
-
PlayerStore.instance.isServiceStarted.value = true
Log.d(tag, radioTag + "created")
}
@@ -303,7 +266,6 @@ class RadioService : MediaBrowserServiceCompat() {
player.release()
unregisterReceiver(receiver)
PlayerStore.instance.currentSong.title.removeObserver(titleObserver)
- PlayerStore.instance.currentSong.startTime.removeObserver(startTimeObserver)
PlayerStore.instance.volume.removeObserver(volumeObserver)
PlayerStore.instance.isPlaying.removeObserver(isPlayingObserver)
PlayerStore.instance.isMuted.removeObserver(isMutedObserver)
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt b/app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt
index a99ae5a..40049a5 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt
@@ -11,11 +11,3 @@ class Tick : TimerTask() {
}
}
-class ApiFetchTick : TimerTask() {
- override fun run() {
- if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_STOPPED)
- {
- PlayerStore.instance.fetchApi()
- }
- }
-}
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt b/app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt
index 4cf734e..8df0546 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt
@@ -31,10 +31,8 @@ class PlayerStore {
val isLpUpdated: MutableLiveData = MutableLiveData()
val isMuted : MutableLiveData = MutableLiveData()
val listenersCount: MutableLiveData = MutableLiveData()
- private val urlToScrape = "https://r-a-d.io/api"
var latencyCompensator : Long = 0
var isInitialized: Boolean = false
- var isStreamDown: Boolean = false
init {
playbackState.value = PlaybackStateCompat.STATE_STOPPED
@@ -47,6 +45,7 @@ class PlayerStore {
isLpUpdated.value = false
isMuted.value = false
currentSong.title.value = noConnectionValue
+ currentSongBackup.title.value = noConnectionValue
listenersCount.value = 0
}
@@ -54,6 +53,7 @@ class PlayerStore {
// ################# API FUNCTIONS ##################
// ##################################################
+ /*
private fun updateApi(resMain: JSONObject, isCompensatingLatency : Boolean = false) {
// If we're not in PLAYING state, update title / artist metadata. If we're playing, the ICY will take care of that.
if (playbackState.value != PlaybackStateCompat.STATE_PLAYING || currentSong.title.value.isNullOrEmpty()
@@ -154,24 +154,26 @@ class PlayerStore {
}
Async(scrape, post)
}
+ */
// ##################################################
// ############## QUEUE / LP FUNCTIONS ##############
// ##################################################
fun updateLp() {
- // note : lp must never be empty. There should always be some songs "last played".
- // if not, then the function has been called before initialization. No need to do anything.
- if (lp.isNotEmpty()){
+ // note : lp is empty at initialization. This check was needed when we used the R/a/dio API.
+ //if (lp.isNotEmpty()){
val n = Song()
n.copy(currentSongBackup)
- lp.add(0, n)
+ if (n.title.value != noConnectionValue)
+ lp.add(0, n)
currentSongBackup.copy(currentSong)
isLpUpdated.value = true
Log.d(tag, playerStoreTag + lp.toString())
- }
+ //}
}
+ /*
fun updateQueue() {
if (queue.isNotEmpty()) {
queue.remove(queue.first())
@@ -257,11 +259,13 @@ class PlayerStore {
song.type.value = songJSON.getInt("type")
return song
}
+ */
// ##################################################
// ############## PICTURE FUNCTIONS #################
// ##################################################
+ /*
private fun fetchPicture(fileUrl: String)
{
val scrape: (Any?) -> Bitmap? = {
@@ -287,10 +291,11 @@ class PlayerStore {
}
Async(scrape, post)
}
+ */
fun initPicture(c: Context) {
streamerPicture.value = BitmapFactory.decodeResource(c.resources,
- R.drawable.actionbar_logo
+ R.drawable.logo_roundsquare
)
}
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt
index f8d8efb..a41b394 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt
@@ -8,19 +8,11 @@ import androidx.appcompat.app.AlertDialog
import androidx.preference.*
import fr.forum_thalie.tsumugi.R
import fr.forum_thalie.tsumugi.preferenceStore
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
class CustomizeFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.customize_preferences, rootKey)
- val userNamePref = preferenceScreen.findPreference("userName")
- userNamePref?.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
- userNamePref?.setOnPreferenceChangeListener { _, newValue ->
- val name = newValue as String
- Requestor.instance.initFavorites(name) // need to be as parameter cause the callback is called BEFORE PARAMETER SET
- true
- }
val snackbarPersistent = preferenceScreen.findPreference("snackbarPersistent")
snackbarPersistent!!.summary = if (preferenceStore.getBoolean("snackbarPersistent", true))
@@ -48,33 +40,5 @@ class CustomizeFragment : PreferenceFragmentCompat() {
true
}
- val helpFavorites = preferenceScreen.findPreference("helpFavorites")
- helpFavorites?.setOnPreferenceClickListener { _ ->
- val url = getString(R.string.github_url_wiki_irc_for_favorites)
- val i = Intent(Intent.ACTION_VIEW)
- i.data = Uri.parse(url)
- startActivity(i)
- true
- }
-
- val fetchPeriod = preferenceScreen.findPreference("fetchPeriod")
- fetchPeriod?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
- fetchPeriod?.setOnPreferenceChangeListener { _, newValue ->
- val builder1 = AlertDialog.Builder(context!!)
- if (Integer.parseInt(newValue as String) == 0)
- builder1.setMessage(R.string.fetch_disabled_restart_the_app)
- else
- builder1.setMessage(R.string.restart_the_app)
- builder1.setCancelable(true)
-
- builder1.setPositiveButton("Close" ) { dialog, _ ->
- dialog.cancel()
- }
-
- val alert11 = builder1.create()
- alert11.show()
- true
- }
-
}
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/preferences/StreamerNotifServiceFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/preferences/StreamerNotifServiceFragment.kt
deleted file mode 100644
index 0383ee1..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/preferences/StreamerNotifServiceFragment.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package fr.forum_thalie.tsumugi.preferences
-
-import android.os.Bundle
-import androidx.appcompat.app.AlertDialog
-import androidx.preference.*
-import fr.forum_thalie.tsumugi.R
-import fr.forum_thalie.tsumugi.preferenceStore
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
-import fr.forum_thalie.tsumugi.streamerNotificationService.stopStreamerMonitor
-
-class StreamerNotifServiceFragment : PreferenceFragmentCompat() {
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- setPreferencesFromResource(R.xml.streamer_notif_service_preferences, rootKey)
-
-
- val streamerPeriod = preferenceScreen.findPreference("streamerMonitorPeriodPref")
-
- val streamerNotification = preferenceScreen.findPreference("newStreamerNotification")
- streamerNotification?.setOnPreferenceChangeListener { _, newValue ->
- if ((newValue as Boolean)) {
- val builder1 = AlertDialog.Builder(context!!)
- builder1.setMessage(R.string.warningStreamerNotif)
- builder1.setCancelable(false)
- builder1.setPositiveButton(
- "Yes"
- ) { dialog, _ ->
- startStreamerMonitor(context!!, force = true) // force enabled because the preference value is not yet set when running this callback.
- streamerPeriod?.isEnabled = true
- dialog.cancel()
- }
-
- builder1.setNegativeButton(
- "No"
- ) { dialog, _ ->
-
- stopStreamerMonitor(context!!)
- (streamerNotification as SwitchPreferenceCompat).isChecked = false
- dialog.cancel()
- }
-
- val alert11 = builder1.create()
- alert11.show()
- }
- else {
- stopStreamerMonitor(context!!)
- streamerPeriod?.isEnabled = false
- WorkerStore.instance.isServiceStarted = false
- }
- true
- }
-
- streamerPeriod?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
- streamerPeriod?.isEnabled = preferenceStore.getBoolean("newStreamerNotification", true)
- streamerPeriod?.setOnPreferenceChangeListener { _, newValue ->
- WorkerStore.instance.tickerPeriod = (Integer.parseInt(newValue as String)).toLong() * 60
- // this should be sufficient, the next alarm schedule should take the new tickerPeriod.
- true
- }
-
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/ServiceNotification.kt b/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/ServiceNotification.kt
deleted file mode 100644
index bd203aa..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/ServiceNotification.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package fr.forum_thalie.tsumugi.streamerNotificationService
-
-import android.content.Context
-import fr.forum_thalie.tsumugi.BaseNotification
-import java.util.*
-
-class ServiceNotification(
- notificationChannelId: String,
- notificationChannel : Int,
- notificationId: Int,
- notificationImportance: Int
-
-) : BaseNotification
- (
- notificationChannelId,
- notificationChannel,
- notificationId,
- notificationImportance
-){
-
- override fun create(c: Context)
- {
- super.create(c)
- update()
- }
-
- fun update()
- {
- val date = Date() // given date
- val calendar = Calendar.getInstance() // creates a new calendar instance
- calendar.time = date // assigns calendar to given date
- val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
- //val hours_american = calendar.get(Calendar.HOUR) // gets hour in 12h format
- val minutes = calendar.get(Calendar.MINUTE)
- val seconds = calendar.get(Calendar.SECOND)
-
- builder.setContentTitle("Never miss a stream! Current: ${WorkerStore.instance.streamerName.value}")
- builder.setContentText("Last update: ${hours}:${minutes}:${seconds}")
- notification = builder.build()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorExtensions.kt b/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorExtensions.kt
deleted file mode 100644
index d3cb66c..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorExtensions.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-package fr.forum_thalie.tsumugi.streamerNotificationService
-
-import android.app.AlarmManager
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.os.Build
-import android.os.SystemClock
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.preference.PreferenceManager
-import fr.forum_thalie.tsumugi.*
-import fr.forum_thalie.tsumugi.alarm.RadioAlarm
-import fr.forum_thalie.tsumugi.playerstore.PlayerStore
-import org.json.JSONObject
-import java.net.URL
-
-fun startNextAlarmStreamer(c: Context){
- // the notification works with an alarm re-scheduled at fixed rate.
- // if the service stopped, the alarm is not re-scheduled.
- if (WorkerStore.instance.isServiceStarted)
- {
- val alarmIntent = Intent(c, StreamerMonitorService::class.java).let { intent ->
- intent.putExtra("action", Actions.NOTIFY.name)
- PendingIntent.getService(c, 0, intent, 0)
- }
-
- val alarmMgr = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
- when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> alarmMgr.setExactAndAllowWhileIdle(
- AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
- alarmIntent
- )
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> alarmMgr.setExact(
- AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
- alarmIntent
- )
- else -> alarmMgr.set(
- AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
- alarmIntent
- )
- }
- } else {
- Log.d(tag, "alarm called while service is dead - skipped.")
- }
-}
-
-fun stopStreamerMonitor(context: Context)
-{
- val intent = Intent(context, StreamerMonitorService::class.java)
- intent.putExtra("action", Actions.KILL.name)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- context.startService(intent)
- } else {
- context.startService(intent)
- }
-
- Log.i(tag, "Service stopped")
-}
-
-fun startStreamerMonitor(context: Context, force: Boolean = false)
-{
- if (!force)
- {
- val isNotifyingForNewStreamer = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("newStreamerNotification", false)
- if (!isNotifyingForNewStreamer)
- return
- }
-
- val intent = Intent(context, StreamerMonitorService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- context.startForegroundService(intent)
- } else {
- context.startService(intent)
- }
-
- Log.i(tag, "Service started on boot")
-}
-
-fun fetchStreamer(applicationContext: Context) {
- val urlToScrape = "https://r-a-d.io/api"
- val scrape : (Any?) -> String =
- {
- URL(urlToScrape).readText()
- }
- val post: (parameter: Any?) -> Unit = {
- val result = JSONObject(it as String)
- if (!result.isNull("main"))
- {
- val name = result.getJSONObject("main").getJSONObject("dj").getString("djname")
- WorkerStore.instance.streamerName.value = name
- }
- }
-
- // notify
- val t = ServiceNotification(
- notificationChannelId = applicationContext.getString(R.string.streamerServiceChannelId),
- notificationChannel = R.string.streamerServiceChannel,
- notificationId = 2,
- notificationImportance = NotificationCompat.PRIORITY_LOW
- )
- t.create(applicationContext)
- t.show()
-
- try{
- Async(scrape, post)
- Log.d(tag, "enqueue next work in ${WorkerStore.instance.tickerPeriod} seconds")
- } catch (e: Exception) {
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorService.kt b/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorService.kt
deleted file mode 100644
index fae27ae..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorService.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-package fr.forum_thalie.tsumugi.streamerNotificationService
-
-
-import android.app.Service
-import android.content.Intent
-import android.os.Build
-import android.os.IBinder
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.lifecycle.Observer
-import androidx.preference.PreferenceManager
-import fr.forum_thalie.tsumugi.Actions
-import fr.forum_thalie.tsumugi.R
-import fr.forum_thalie.tsumugi.tag
-import java.util.*
-
-class StreamerMonitorService : Service() {
- override fun onBind(intent: Intent): IBinder? {
- return null // no binding allowed nor needed
- }
- private val streamerNameObserver: Observer = Observer {
- val previousStreamer: String
- if (PreferenceManager.getDefaultSharedPreferences(this).contains("streamerName"))
- {
- previousStreamer = PreferenceManager.getDefaultSharedPreferences(this).getString("streamerName", "") ?: ""
- /* 3 conditions:
- - the streamer changed from previously
- - there is a previous non-empty streamer (at least second time running it)
- - the current streamer is non-empty (this can happen at Activity start where init() is called)
- */
- if (previousStreamer != it && previousStreamer != "" && it != "")
- {
- // notify
- val newStreamer = StreamerNotification(
- notificationChannelId = this.getString(R.string.streamerNotificationChannelId),
- notificationChannel = R.string.streamerNotificationChannel,
- notificationId = 3,
- notificationImportance = NotificationCompat.PRIORITY_DEFAULT
- )
- newStreamer.create(this)
- newStreamer.show()
- }
- }
-
- with(PreferenceManager.getDefaultSharedPreferences(this).edit()){
- putString("streamerName", it)
- commit()
- }
- }
-
- override fun onCreate() {
- super.onCreate()
- val streamerMonitorNotification = ServiceNotification(
- notificationChannelId = this.getString(R.string.streamerServiceChannelId),
- notificationChannel = R.string.streamerServiceChannel,
- notificationId = 2,
- notificationImportance = NotificationCompat.PRIORITY_LOW
- )
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- {
- streamerMonitorNotification.create(this)
- streamerMonitorNotification.update()
- streamerMonitorNotification.show()
- startForeground(2, streamerMonitorNotification.notification)
- }
-
- WorkerStore.instance.tickerPeriod = 60 *
- (if (PreferenceManager.getDefaultSharedPreferences(this).contains("streamerMonitorPeriodPref"))
- Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(this).getString("streamerMonitorPeriodPref", "15")!!).toLong()
- else
- 15)
- Log.d(tag, "tickerPeriod = ${WorkerStore.instance.tickerPeriod}")
-
- with(PreferenceManager.getDefaultSharedPreferences(this).edit()){
- remove("streamerName")
- commit() // I commit on main thread to be sure it's been updated before continuing.
- }
- WorkerStore.instance.streamerName.observeForever(streamerNameObserver)
- WorkerStore.instance.isServiceStarted = true
- startNextAlarmStreamer(this)
- Log.d(tag, "streamerMonitor created")
- }
-
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- val isNotifyingForNewStreamer = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("newStreamerNotification", false)
-
- // it's probably redundant but it shouldn't hurt
- if (!isNotifyingForNewStreamer || !WorkerStore.instance.isServiceStarted)
- {
- stopForeground(true)
- stopSelf()
- return START_NOT_STICKY
- }
- when (intent?.getStringExtra("action")) {
- Actions.NOTIFY.name -> {
- val date = Date() // given date
- val calendar = Calendar.getInstance() // creates a new calendar instance
- calendar.time = date // assigns calendar to given date
- val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
- //val hours_american = calendar.get(Calendar.HOUR) // gets hour in 12h format
- val minutes = calendar.get(Calendar.MINUTE) // gets month number, NOTE this is zero based!
-
- Log.d(tag, "Fetched streamer name at ${hours}:${if (minutes < 10) "0" else ""}${minutes}")
- fetchStreamer(this)
- startNextAlarmStreamer(this) // schedule next alarm
- return START_STICKY
- }
- Actions.KILL.name -> {
- stopForeground(true)
- stopSelf()
- return START_NOT_STICKY
- }
- }
- return START_STICKY
- //super.onStartCommand(intent, flags, startId)
- }
-
- override fun onDestroy() {
- WorkerStore.instance.streamerName.removeObserver(streamerNameObserver)
- WorkerStore.instance.isServiceStarted = false
- super.onDestroy()
- }
-}
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerNotification.kt b/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerNotification.kt
deleted file mode 100644
index f40c028..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerNotification.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package fr.forum_thalie.tsumugi.streamerNotificationService
-
-import android.content.Context
-import fr.forum_thalie.tsumugi.BaseNotification
-import java.util.*
-
-class StreamerNotification(
- notificationChannelId: String,
- notificationChannel : Int,
- notificationId: Int,
- notificationImportance: Int
-
-) : BaseNotification
- (
- notificationChannelId,
- notificationChannel,
- notificationId,
- notificationImportance
-){
- override fun create(c: Context) {
- super.create(c)
- val date = Date() // given date
- val calendar = Calendar.getInstance() // creates a new calendar instance
- calendar.time = date // assigns calendar to given date
- val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
- //val hours_american = calendar.get(Calendar.HOUR) // gets hour in 12h format
- val minutes = calendar.get(Calendar.MINUTE) // gets month number, NOTE this is zero based!
-
- builder.setContentTitle("${WorkerStore.instance.streamerName.value} started streaming!")
- builder.setContentText("Started at ${hours}:${if (minutes < 10) "0" else ""}${minutes}")
- builder.setAutoCancel(true)
- super.show()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/WorkerStore.kt b/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/WorkerStore.kt
deleted file mode 100644
index b20b50d..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/WorkerStore.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package fr.forum_thalie.tsumugi.streamerNotificationService
-
-import android.content.Context
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
-import androidx.preference.PreferenceManager
-import fr.forum_thalie.tsumugi.R
-import fr.forum_thalie.tsumugi.tag
-
-class WorkerStore {
- companion object {
- val instance = WorkerStore()
- }
-
- val streamerName = MutableLiveData()
- var isServiceStarted : Boolean = false
- var tickerPeriod : Long = 45 // seconds
-
- init {
- tickerPeriod = 45
- streamerName.value = ""
- isServiceStarted = false
- }
-
- fun init(c: Context)
- {
- tickerPeriod = 45
- streamerName.value = ""
- val tickerPeriod = 60 *
- (if (PreferenceManager.getDefaultSharedPreferences(c).contains("streamerMonitorPeriodPref"))
- Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(c).getString("streamerMonitorPeriodPref", "15")!!).toLong()
- else
- 15
- )
- instance.tickerPeriod = tickerPeriod
- Log.d(tag, "tickerPeriod = $tickerPeriod")
-
- with(PreferenceManager.getDefaultSharedPreferences(c).edit()){
- remove("streamerName")
- commit() // I commit on main thread to be sure it's been updated before continuing.
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatFragment.kt
deleted file mode 100644
index 89718b1..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatFragment.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.chat
-
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.webkit.WebView
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProviders
-import fr.forum_thalie.tsumugi.R
-
-
-class ChatFragment : Fragment() {
-
- private lateinit var chatViewModel: ChatViewModel
-
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
-
- chatViewModel =
- ViewModelProviders.of(this).get(ChatViewModel::class.java)
-
- if (!chatViewModel.isChatLoaded)
- {
-
- try {
- chatViewModel.root = inflater.inflate(R.layout.fragment_chat, container, false)
- chatViewModel.webView = chatViewModel.root.findViewById(R.id.chat_webview)
- chatViewModel.webViewChat = WebViewChat(chatViewModel.webView as WebView)
- chatViewModel.webViewChat!!.start()
- } catch (e: Exception) {
- chatViewModel.root = inflater.inflate(R.layout.fragment_error_chat, container, false)
- }
-
- chatViewModel.isChatLoaded = true
- Log.d(tag, "webview created")
- } else {
- Log.d(tag, "webview already created!?")
- }
-
- return chatViewModel.root
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatViewModel.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatViewModel.kt
deleted file mode 100644
index 0a4a1aa..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatViewModel.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.chat
-
-import android.view.View
-import android.webkit.WebView
-import androidx.lifecycle.ViewModel
-
-class ChatViewModel : ViewModel() {
- lateinit var root: View
- var webView: WebView? = null
- var isChatLoaded = false
- var webViewChat: WebViewChat? = null
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/WebViewChat.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/WebViewChat.kt
deleted file mode 100644
index 580c617..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/WebViewChat.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.chat
-
-import android.annotation.SuppressLint
-import android.content.Intent
-import android.net.Uri
-import android.webkit.WebChromeClient
-import android.webkit.WebView
-
-class WebViewChat(private val webView: WebView) {
-
- @SuppressLint("SetJavaScriptEnabled")
- fun start() {
-
- val webSetting = this.webView.settings
- webSetting.javaScriptEnabled = true
- webSetting.setSupportZoom(false)
-
- /* TODO: in the future, it could be nice to have a parameters screen where you can:
- - Set the text zoom
- - Set your username (to not type it every time, would it be possible?)
- - Hide the chat?
- - do more? */
- webSetting.textZoom = 90
-
- webSetting.setSupportMultipleWindows(true)
- // needs to open target="_blank" links as KiwiIRC links have this attribute.
- // shamelessly ripped off https://stackoverflow.com/questions/18187714/android-open-target-blank-links-in-webview-with-external-browser
- this.webView.webChromeClient = object : WebChromeClient() {
- override fun onCreateWindow(
- view: WebView,
- dialog: Boolean,
- userGesture: Boolean,
- resultMsg: android.os.Message
- ): Boolean {
- val result = view.hitTestResult
- val data = result.extra
- val context = view.context
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(data))
- context.startActivity(browserIntent)
- return false
- }
- }
-
- webView.loadUrl("file:///android_asset/chat.html")
- }
-
- }
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt
index 1e68823..2ebc21d 100644
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt
+++ b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt
@@ -5,62 +5,19 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
-import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
import androidx.viewpager.widget.ViewPager
-import com.google.android.material.snackbar.BaseTransientBottomBar
-import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import fr.forum_thalie.tsumugi.R
-import fr.forum_thalie.tsumugi.preferenceStore
import fr.forum_thalie.tsumugi.ui.songs.queuelp.LastPlayedFragment
-import fr.forum_thalie.tsumugi.ui.songs.queuelp.QueueFragment
-import fr.forum_thalie.tsumugi.ui.songs.request.FavoritesFragment
-import fr.forum_thalie.tsumugi.ui.songs.request.RequestFragment
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
class SongsFragment : Fragment() {
private lateinit var adapter : SongsPagerAdapter
- private lateinit var snackBar : Snackbar
private lateinit var root: View
private lateinit var viewPager: ViewPager
-
- private val snackBarTextObserver: Observer = Observer {
- if (Requestor.instance.snackBarText.value != "")
- {
- val snackBarLength = if (preferenceStore.getBoolean("snackbarPersistent", true))
- Snackbar.LENGTH_INDEFINITE
- else Snackbar.LENGTH_LONG
- snackBar = Snackbar.make(viewPager, "", snackBarLength)
-
- if (snackBarLength == Snackbar.LENGTH_INDEFINITE)
- snackBar.setAction("OK") {
- snackBar.dismiss()
- }
-
- snackBar.behavior = BaseTransientBottomBar.Behavior().apply {
- setSwipeDirection(BaseTransientBottomBar.Behavior.SWIPE_DIRECTION_ANY)
- }
-
- val snackBarView = snackBar.view
- val textView =
- snackBarView.findViewById(com.google.android.material.R.id.snackbar_text) as TextView
- if (Requestor.instance.addRequestMeta != "")
- textView.maxLines = 4
- else
- textView.maxLines = 2
- snackBar.setText((it as CharSequence))
- snackBar.show()
- Requestor.instance.snackBarText.value = "" // resetting afterwards to avoid re-triggering it when we enter again the fragment
- Requestor.instance.addRequestMeta = ""
- }
- }
-
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -71,9 +28,8 @@ class SongsFragment : Fragment() {
viewPager = root.findViewById(R.id.tabPager)
adapter = SongsPagerAdapter(childFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
adapter.addFragment(LastPlayedFragment.newInstance(), "last played")
- adapter.addFragment(QueueFragment.newInstance(), "queue")
- adapter.addFragment(RequestFragment.newInstance(), "request")
- adapter.addFragment(FavoritesFragment.newInstance(), "Favorites")
+
+ //adapter.addFragment(QueueFragment.newInstance(), "queue")
viewPager.adapter = adapter
@@ -81,14 +37,8 @@ class SongsFragment : Fragment() {
tabLayout.setupWithViewPager(viewPager)
Log.d(tag, "SongFragment view created")
- Requestor.instance.snackBarText.observeForever(snackBarTextObserver)
return root
}
- override fun onDestroyView() {
- Requestor.instance.snackBarText.removeObserver(snackBarTextObserver)
- super.onDestroyView()
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/QueueFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/QueueFragment.kt
deleted file mode 100644
index 72a7b7f..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/QueueFragment.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.queuelp
-
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import fr.forum_thalie.tsumugi.R
-import fr.forum_thalie.tsumugi.playerstore.PlayerStore
-import fr.forum_thalie.tsumugi.playerstore.Song
-
-class QueueFragment : Fragment(){
- private val lastPlayedFragmentTag = this::class.java.name
-
- private lateinit var recyclerView: RecyclerView
- private lateinit var viewAdapter: RecyclerView.Adapter<*>
- private lateinit var viewManager: RecyclerView.LayoutManager
-
-
- private val queueObserver = Observer {
- Log.d(tag, lastPlayedFragmentTag + "queue changed")
- viewAdapter.notifyDataSetChanged()
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- // Inflate the layout for this fragment
- val root = inflater.inflate(R.layout.fragment_last_played, container, false)
-
- viewManager = LinearLayoutManager(context)
- viewAdapter = SongAdaptater(
- if (PlayerStore.instance.queue.isEmpty())
- ArrayList(listOf((Song("No queue - "))))
- else
- PlayerStore.instance.queue
- )
-
- recyclerView = root.findViewById(R.id.queue_lp_recycler).apply {
- // use this setting to improve performance if you know that changes
- // in content do not change the layout size of the RecyclerView
- setHasFixedSize(true)
-
- // use a linear layout manager
- layoutManager = viewManager
-
- // specify an viewAdapter (see also next example)
- adapter = viewAdapter
- }
-
- PlayerStore.instance.isQueueUpdated.observeForever(queueObserver)
-
- return root
- }
-
- override fun onDestroyView() {
- PlayerStore.instance.isQueueUpdated.removeObserver(queueObserver)
- super.onDestroyView()
- }
-
- companion object {
- @JvmStatic
- fun newInstance() = QueueFragment()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/CooldownCalculator.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/CooldownCalculator.kt
deleted file mode 100644
index 64175fe..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/CooldownCalculator.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import kotlin.math.exp
-import kotlin.math.max
-
-/*
-//PHP cooldown calculator
-
-function pretty_cooldown($lp, $lr, $rc) {
- $delay = delay($rc);
- $now = time();
- $cd = intval(max($lp + $delay - $now, $lr + $delay - $now));
- if ($cd <= 0)
- return "Request";
- $days = intdiv_1($cd, 86400);
- $cd = $cd % 86400;
- $hours = intdiv_1($cd, 3600);
- $cd = $cd % 3600;
- $minutes = intdiv_1($cd, 60);
- $seconds = $cd % 60;
- if ($days > 0)
- return "Requestable in ".$days."d".$hours."h";
- else if ($hours > 0)
- return "Requestable in ".$hours."h".$minutes."m";
- else if ($minutes > 0)
- return "Requestable in ".$minutes."m".$seconds."s";
- return "Request";
-}
-function requestable($lastplayed, $requests) {
- $delay = delay($requests);
- return (time() - $lastplayed) > $delay;
-}
-function delay($priority) {
- // priority is 30 max
- if ($priority > 30)
- $priority = 30;
- // between 0 and 7 return magic
- if ($priority >= 0 and $priority <= 7)
- $cd = -11057 * $priority * $priority + 172954 * $priority + 81720;
- // if above that, return magic crazy numbers
- else
- $cd = (int) (599955 * exp(0.0372 * $priority) + 0.5);
- return $cd / 2;
-}
- */
-
-// this function implements the magic delay used on R/a/dio website:
-// https://github.com/R-a-dio/site/blob/develop/app/start/global.php#L125
-// (Seriously guys, what were you thinking with these crazy magic numbers...)
-fun delay(rawPriority: Int) : Int {
- val priority = if (rawPriority > 30) 30 else rawPriority
- val coolDown : Int =
- if (priority in 0..7)
- -11057 * priority * priority + 172954 * priority + 81720
- else
- (599955 * exp(0.0372 * (priority.toDouble()) + 0.5)).toInt()
- return coolDown/2
-}
-
-// I tweaked this to report in a single point whether the song is requestable or not
-fun coolDown(lastPlayed: Int?, lastRequest: Int?, requestsNbr: Int?) : Long {
- if (requestsNbr == null || lastPlayed == null || lastRequest == null)
- return Long.MAX_VALUE // maximum positive value : the song won't be requestable
-
- val delay = delay(requestsNbr)
- val now = (System.currentTimeMillis() / 1000)
- return max(lastPlayed, lastRequest) + delay - now
- // if coolDown < 0, the song is requestable.
-}
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/FavoritesFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/FavoritesFragment.kt
deleted file mode 100644
index 2631a7b..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/FavoritesFragment.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.SearchView
-import android.widget.TextView
-import androidx.appcompat.widget.AppCompatButton
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import fr.forum_thalie.tsumugi.*
-
-
-class FavoritesFragment : Fragment() {
-
- private lateinit var recyclerView: RecyclerView
- private lateinit var viewAdapter: RecyclerView.Adapter<*>
- private lateinit var viewManager: RecyclerView.LayoutManager
- private lateinit var searchView: SearchView
- private lateinit var root: View
- private lateinit var recyclerSwipe: SwipeRefreshLayout
-
- private val favoritesSongObserver : Observer = Observer {
- viewAdapter.notifyDataSetChanged()
- createView(isCallback = true) // force-re-create the view, but do not call again the initFavorites (avoid callback loop)
- recyclerSwipe.isRefreshing = false // disable refreshing animation. Needs to be done manually...
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- root = inflater.inflate(R.layout.fragment_request, container, false)
-
- return createView()
- }
-
- private fun createView(isCallback: Boolean = false) : View?
- {
-
- viewAdapter = RequestSongAdapter(Requestor.instance.favoritesSongArray)
-
- val listener : SearchView.OnQueryTextListener = object : SearchView.OnQueryTextListener{
- override fun onQueryTextSubmit(query: String?): Boolean {
- // do nothing
- return true
- }
- override fun onQueryTextChange(newText: String?): Boolean {
- (viewAdapter as RequestSongAdapter).filter(newText ?: "")
- return true
- }
- }
-
- searchView = root.findViewById(R.id.searchBox)
- searchView.queryHint = "Search filter..."
- searchView.setOnQueryTextListener(listener)
- viewManager = LinearLayoutManager(context)
- recyclerView = root.findViewById(R.id.request_recycler).apply {
- // use this setting to improve performance if you know that changes
- // in content do not change the layout size of the RecyclerView
- setHasFixedSize(true)
-
- // use a linear layout manager
- layoutManager = viewManager
-
- // specify an viewAdapter (see also next example)
- adapter = viewAdapter
- }
-
- val noUserNameText : TextView = root.findViewById(R.id.noUserNameText)
-
- recyclerSwipe = root.findViewById(R.id.recyclerSwipe) as SwipeRefreshLayout
- recyclerSwipe.setOnRefreshListener {
- val userName1 = preferenceStore.getString("userName", null)
- Log.d(tag,"userName = $userName1")
- if (userName1 != null && !userName1.isBlank())
- {
- noUserNameText.visibility = View.GONE
- Requestor.instance.initFavorites()
- } else {
- noUserNameText.visibility = View.VISIBLE
- recyclerSwipe.isRefreshing = false
- }
- }
-
-
- val userName1 = preferenceStore.getString("userName", null)
- Log.d(tag,"userName = $userName1")
- if (userName1 != null && !userName1.isBlank())
- {
- noUserNameText.visibility = View.GONE
- if (!isCallback) // avoid callback loop if called from the Observer.
- Requestor.instance.initFavorites()
- } else {
- noUserNameText.visibility = View.VISIBLE
- recyclerSwipe.isRefreshing = false
- }
-
- val raFButton : AppCompatButton = root.findViewById(R.id.ra_f_button)
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) // for API21+ Material Design makes ripples on the button.
- raFButton.supportBackgroundTintList = colorGreenList
- else // But on API20- no Material Design support, so we add some more color when clicked
- raFButton.supportBackgroundTintList = colorGreenListCompat
- raFButton.isEnabled = true
- raFButton.isClickable = true
- raFButton.setOnClickListener {
- val s = Requestor.instance.raF()
- Requestor.instance.snackBarText.value = ""
- Requestor.instance.addRequestMeta = "Request: ${s.artist.value} - ${s.title.value}\n"
- Requestor.instance.request(s.id)
- }
- raFButton.visibility = View.VISIBLE
-
- Requestor.instance.isFavoritesUpdated.observe(viewLifecycleOwner, favoritesSongObserver)
- return root
- }
-
-
- companion object {
- @JvmStatic
- fun newInstance() = FavoritesFragment()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestFragment.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestFragment.kt
deleted file mode 100644
index fa19627..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestFragment.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.SearchView
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import fr.forum_thalie.tsumugi.R
-
-class RequestFragment : Fragment() {
-
- private lateinit var recyclerView: RecyclerView
- private lateinit var viewAdapter: RecyclerView.Adapter<*>
- private lateinit var viewManager: RecyclerView.LayoutManager
- private lateinit var searchView: SearchView
-
- private val listener : SearchView.OnQueryTextListener = object : SearchView.OnQueryTextListener{
- override fun onQueryTextSubmit(query: String?): Boolean {
- if (query == null || query.isEmpty())
- Requestor.instance.snackBarText.value = "Field is empty, no search possible."
- else
- Requestor.instance.search(query)
- return true
- }
- override fun onQueryTextChange(newText: String?): Boolean {
- if (newText == "")
- {
- Requestor.instance.reset()
- viewAdapter.notifyDataSetChanged() // this is to remove the "Load more" button
- }
- return true
- }
- }
-
- private val requestSongObserver = Observer {
- Log.d(tag, "request song list changed")
- viewAdapter.notifyDataSetChanged()
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- // Inflate the layout for this fragment
- val root = inflater.inflate(R.layout.fragment_request, container, false)
-
- val recyclerSwipe = root.findViewById(R.id.recyclerSwipe) as SwipeRefreshLayout
- recyclerSwipe.isEnabled = false // don't need to pull-to-refresh for Request
-
- searchView = root.findViewById(R.id.searchBox)
- searchView.setOnQueryTextListener(listener)
-
- viewManager = LinearLayoutManager(context)
- viewAdapter = RequestSongAdapter(Requestor.instance.requestSongArray)
-
- recyclerView = root.findViewById(R.id.request_recycler).apply {
- // use this setting to improve performance if you know that changes
- // in content do not change the layout size of the RecyclerView
- setHasFixedSize(true)
-
- // use a linear layout manager
- layoutManager = viewManager
-
- // specify an viewAdapter (see also next example)
- adapter = viewAdapter
- }
- Requestor.instance.isRequestResultUpdated.observe(viewLifecycleOwner, requestSongObserver)
- return root
- }
-
- companion object {
- @JvmStatic
- fun newInstance() = RequestFragment()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestResponse.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestResponse.kt
deleted file mode 100644
index a4c1e73..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestResponse.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import fr.forum_thalie.tsumugi.playerstore.Song
-import org.json.JSONObject
-
-class RequestResponse(jsonResponse: JSONObject) {
- //val total: Int = jsonResponse.getInt("total")
- //val perPage: Int = jsonResponse.getInt("per_page") // should stay 20 but in any case...
- val currentPage: Int = jsonResponse.getInt("current_page")
- val lastPage: Int = jsonResponse.getInt("last_page")
- //val fromNbr: Int = jsonResponse.getInt("from")
- //val toNbr: Int = jsonResponse.getInt("to")
- var songs : ArrayList = ArrayList()
-
- init {
- val songList = jsonResponse.getJSONArray("data")
- for (i in 0 until songList.length())
- {
-
- val title = (songList[i] as JSONObject).getString("title")
- val artist = (songList[i] as JSONObject).getString("artist")
- val id = (songList[i] as JSONObject).getInt("id")
-
- val s = Song("", id)
- s.title.value = title
- s.artist.value = artist
- s.isRequestable = (songList[i] as JSONObject).getBoolean("requestable")
- // TODO add the time before being requestable.
- songs.add(s)
- }
- }
-
- override fun toString(): String {
- var s = ""
- for (i in 0 until songs.size)
- {
- s += (songs[i].artist.value + " - " + songs[i].title.value + " | ")
- }
- return s
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestSongAdapter.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestSongAdapter.kt
deleted file mode 100644
index 6f72f8a..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestSongAdapter.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import android.annotation.SuppressLint
-import android.os.Build
-import android.util.TypedValue
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.widget.TextViewCompat
-import androidx.recyclerview.widget.RecyclerView
-import fr.forum_thalie.tsumugi.*
-import fr.forum_thalie.tsumugi.playerstore.Song
-import kotlinx.android.synthetic.main.request_song_view.view.*
-import android.view.View
-import kotlinx.android.synthetic.main.button_load_more.view.*
-import android.R.attr.name
-import android.text.method.TextKeyListener.clear
-import android.util.Log
-import java.util.*
-import kotlin.collections.ArrayList
-
-
-class RequestSongAdapter(private val dataSet: ArrayList
- /*,
- context: Context,
- resource: Int,
- objects: Array*/
-) : RecyclerView.Adapter() /*ArrayAdapter(context, resource, objects)*/ {
-
- private val viewTypeCell = 1 // normal cell with song and request button
- private val viewTypeFooter = 2 // the bottom cell should be the "load more" button whenever needed
-
- // Provide a reference to the views for each data item
- // Complex data items may need more than one view per item, and
- // you provide access to all the views for a data item in a view holder.
- // Each data item is just a string in this case that is shown in a TextView.
- class MyViewHolder(view: ConstraintLayout) : RecyclerView.ViewHolder(view)
-
-
- // Create new views (invoked by the layout manager)
- override fun onCreateViewHolder(parent: ViewGroup,
- viewType: Int): MyViewHolder {
- // create a new view
-
- val view =
- if (viewType == viewTypeCell)
- LayoutInflater.from(parent.context).inflate(R.layout.request_song_view, parent, false) as ConstraintLayout
- else
- LayoutInflater.from(parent.context).inflate(R.layout.button_load_more, parent, false) as ConstraintLayout
-
- // set the view's size, margins, paddings and layout parameters
- //...
- return MyViewHolder(view)
- }
-
- // Replace the contents of a view (invoked by the layout manager)
- @SuppressLint("SetTextI18n")
- override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
-
- if (itemCount <= 1)
- {
- // in any case, if there's nothing, don't display the loadMore button!!
- holder.itemView.loadMoreButton.visibility = View.GONE
- return
- }
-
- if (holder.itemViewType == viewTypeFooter)
- {
- if (Requestor.instance.isLoadMoreVisible)
- holder.itemView.loadMoreButton.visibility = View.VISIBLE
- else
- holder.itemView.loadMoreButton.visibility = View.GONE
- holder.itemView.loadMoreButton.text = "Load more results"
- holder.itemView.loadMoreButton.setOnClickListener{
- Requestor.instance.loadMore()
- }
- return
- }
-
- val artist = holder.itemView.findViewById(R.id.request_song_artist)
- val title = holder.itemView.findViewById(R.id.request_song_title)
- val button = holder.itemView.request_button
-
- if (dataSet[position].isRequestable) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) // for API21+ Material Design makes ripples on the button.
- button.supportBackgroundTintList = colorGreenList
- else // But on API20- no Material Design support, so we add some more color when clicked
- button.supportBackgroundTintList = colorGreenListCompat
- button.isEnabled = true
- button.isClickable = true
- button.setOnClickListener {
- Requestor.instance.request(dataSet[position].id)
- }
- } else {
- button.supportBackgroundTintList = colorRedList
- button.isEnabled = false
- button.isClickable = false
- }
-
- TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
- button,2, 24, 2, TypedValue.COMPLEX_UNIT_SP)
- artist.text = dataSet[position].artist.value
- title.text = dataSet[position].title.value
- }
-
- // Return the size of your dataset (invoked by the layout manager)
- override fun getItemCount() = dataSet.size + 1 // add 1 for the "Load more results" button
-
- override fun getItemViewType(position: Int): Int {
- return if (position == dataSet.size) viewTypeFooter else viewTypeCell
- }
-
- /*
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- // create a new view
- val view = LayoutInflater.from(parent.context)
- .inflate(R.layout.song_view, parent, false) as ConstraintLayout
- }
- */
-
- // a filtering function. As naive as it could be, but it should work.
- private val dataSetOrig = ArrayList()
- init {
- dataSetOrig.addAll(dataSet)
- }
-
- fun filter(entry: String) {
- var text = entry
- dataSet.clear()
- Log.d(tag, "entering filter")
- if (text.isEmpty()) {
- dataSet.addAll(dataSetOrig)
- } else {
- text = text.toLowerCase(locale = Locale.ROOT)
- for (item in dataSetOrig) {
- Log.d(tag, "$text, ${item.artist.value!!.toLowerCase(locale = Locale.ROOT)}, ${item.title.value!!.toLowerCase(locale = Locale.ROOT)}")
- if (item.artist.value!!.toLowerCase(locale = Locale.ROOT).contains(text) ||
- item.title.value!!.toLowerCase(locale = Locale.ROOT).contains(text)) {
- dataSet.add(item)
- }
- }
- }
- notifyDataSetChanged()
- }
-
-}
-
diff --git a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/Requestor.kt b/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/Requestor.kt
deleted file mode 100644
index a9259b2..0000000
--- a/app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/Requestor.kt
+++ /dev/null
@@ -1,301 +0,0 @@
-package fr.forum_thalie.tsumugi.ui.songs.request
-
-import android.util.Log
-import androidx.lifecycle.MutableLiveData
-import fr.forum_thalie.tsumugi.ActionOnError
-import fr.forum_thalie.tsumugi.Async
-import fr.forum_thalie.tsumugi.playerstore.Song
-import fr.forum_thalie.tsumugi.preferenceStore
-import fr.forum_thalie.tsumugi.tag
-import org.json.JSONArray
-import org.json.JSONException
-import org.json.JSONObject
-import java.io.BufferedReader
-import java.io.IOException
-import java.io.InputStreamReader
-import java.net.CookieHandler
-import java.net.CookieManager
-import java.net.MalformedURLException
-import java.net.URL
-import java.util.*
-import java.util.regex.Pattern
-
-import javax.net.ssl.HttpsURLConnection
-import kotlin.collections.ArrayList
-import kotlin.random.Random
-
-/**
- * Requests a song via the website's API
- *
- * We scrape the website for a CSRF token and POST it to /request/ endpoint with
- * the song id
- *
- * Created by Kethsar on 1/2/2017.
- * Converted to Kotlin and adapted by Yattoz on 05 Nov. 2019
- */
-
-class Requestor {
- var addRequestMeta: String = ""
- private val cookieManager: CookieManager = CookieManager()
- private val requestUrl = "https://r-a-d.io/request/%1\$d"
- private val searchUrl = "https://r-a-d.io/api/search/%1s?page=%2\$d"
- private val favoritesUrl = "https://r-a-d.io/faves/%1s?dl=true"
- private val songThresholdStep = 50
- private var songThreshold = songThresholdStep
- private var localQuery = ""
-
- private var token: String? = null
- val snackBarText : MutableLiveData = MutableLiveData()
- private var responseArray : ArrayList = ArrayList()
- val requestSongArray : ArrayList = ArrayList()
- val favoritesSongArray : ArrayList = ArrayList()
- val isRequestResultUpdated : MutableLiveData = MutableLiveData()
- val isFavoritesUpdated : MutableLiveData = MutableLiveData()
- var isLoadMoreVisible: Boolean = false
-
-
- init {
- snackBarText.value = ""
- isRequestResultUpdated.value = false
- isFavoritesUpdated.value = false
- isLoadMoreVisible = false
- }
-
- fun initFavorites(userName : String? = preferenceStore.getString("userName", null)){
- Log.d(tag, "initializing favorites")
- favoritesSongArray.clear()
- if (userName == null)
- {
- // Display is done by default in the XML.
- Log.d(tag, "no user name set for favorites")
- isFavoritesUpdated.value = true
- return
- }
- val favoritesUserUrl = String.format(Locale.getDefault(), favoritesUrl, userName)
- val scrapeFavorites : (Any?) -> JSONArray = {
- JSONArray(URL(favoritesUserUrl).readText())
- }
- val postFavorites : (Any?) -> Unit = {
- val res = it as JSONArray
- for (i in 0 until (res).length())
- {
- val item = res.getJSONObject(i)
- val artistTitle = item.getString("meta")
- val id : Int? = if (item.isNull("tracks_id"))
- null
- else
- item.getInt("tracks_id")
-
- val lastRequested : Int? = if (item.isNull("lastrequested")) null else item.getInt("lastrequested")
- val lastPlayed : Int? = if (item.isNull("lastplayed")) null else item.getInt("lastplayed")
- val requestCount : Int? = if (item.isNull("requestcount")) null else item.getInt("requestcount")
- val isRequestable = (coolDown(lastPlayed, lastRequested, requestCount) < 0)
- //Log.d(tag, "val : $id")
- favoritesSongArray.add(Song(artistTitle, id ?: 0, isRequestable))
- }
- Log.d(tag, "favorites : $favoritesSongArray")
- isFavoritesUpdated.value = true
- }
- Async(scrapeFavorites, postFavorites, ActionOnError.NOTIFY)
- }
-
- fun search(query: String)
- {
- responseArray.clear()
- requestSongArray.clear()
- localQuery = query
- searchPage(query, 1) // the searchPage function is recursive to get all pages.
- }
-
- private fun searchPage(query: String, pageNumber : Int)
- {
- val searchURL = String.format(Locale.getDefault(), searchUrl, query, pageNumber)
- val scrape : (Any?) -> JSONObject = {
- val res = URL(searchURL).readText()
- val json = JSONObject(res)
- json
- }
- val post : (Any?) -> Unit = {
- val response = RequestResponse(it as JSONObject)
-
- responseArray.add(response)
- for (i in 0 until response.songs.size)
- {
- requestSongArray.add(response.songs[i])
- }
- isRequestResultUpdated.value = true
- if (requestSongArray.size >= songThreshold)
- {
- isLoadMoreVisible = true
-
- } else {
- if (response.currentPage < response.lastPage)
- searchPage(query, pageNumber + 1) // recursive call to get the next page
- else
- finishSearch()
- }
-
- }
- Async(scrape, post, ActionOnError.NOTIFY)
- }
-
- private fun finishSearch()
- {
- isLoadMoreVisible = false
- }
-
- fun reset()
- {
- requestSongArray.clear()
- responseArray.clear()
- isRequestResultUpdated.value = false
- songThreshold = songThresholdStep
- }
-
- fun loadMore()
- {
- songThreshold += songThresholdStep
- searchPage(localQuery, responseArray.last().currentPage + 1)
- }
-
-
- /**
- * Scrape the website for the CSRF token required for requesting
- * scrapeToken and postToken are the two lambas run by the Async() class.
- */
-
- private val scrapeToken : (Any?) -> Any? = {
- val radioSearchUrl = "https://r-a-d.io/search"
- var searchURL: URL? = null
- var retVal: String? = null
- var reader: BufferedReader? = null
-
- CookieHandler.setDefault(cookieManager) // it[0] ??
-
- try {
- searchURL = URL(radioSearchUrl)
- } catch (e: MalformedURLException) {
- e.printStackTrace()
- }
-
- try {
- reader = BufferedReader(InputStreamReader(searchURL!!.openStream(), "UTF-8"))
- var line: String?
- line = reader.readLine()
- while (line != null)
- {
- line = line.trim { it <= ' ' }
- val p = Pattern.compile("value=\"(\\w+)\"")
- val m = p.matcher(line)
-
- if (line.startsWith("