Browse Source

Merge pull request #2 from yattoz/planning

yattoz 5 years ago
parent
commit
9fa5e38c80
35 changed files with 744 additions and 62 deletions
  1. 66 0
      app/src/main/assets/planning_example.json
  2. 1 1
      app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt
  3. 11 3
      app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt
  4. 2 0
      app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt
  5. 15 2
      app/src/main/java/fr/forum_thalie/tsumugi/Values.kt
  6. 4 3
      app/src/main/java/fr/forum_thalie/tsumugi/alarm/RadioAlarm.kt
  7. 109 0
      app/src/main/java/fr/forum_thalie/tsumugi/planning/Planning.kt
  8. 92 0
      app/src/main/java/fr/forum_thalie/tsumugi/planning/Programme.kt
  9. 13 13
      app/src/main/java/fr/forum_thalie/tsumugi/preferences/AlarmFragment.kt
  10. 2 2
      app/src/main/java/fr/forum_thalie/tsumugi/ui/APagerAdapter.kt
  11. 11 4
      app/src/main/java/fr/forum_thalie/tsumugi/ui/nowplaying/NowPlayingFragment.kt
  12. 72 0
      app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeAdapter.kt
  13. 75 0
      app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeDayFragment.kt
  14. 8 0
      app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeDayViewModel.kt
  15. 49 0
      app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeFragment.kt
  16. 8 5
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt
  17. 2 0
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/SongAdaptater.kt
  18. 11 0
      app/src/main/res/drawable-anydpi/ic_programme.xml
  19. BIN
      app/src/main/res/drawable-hdpi/ic_programme.png
  20. BIN
      app/src/main/res/drawable-mdpi/ic_programme.png
  21. BIN
      app/src/main/res/drawable-xhdpi/ic_programme.png
  22. BIN
      app/src/main/res/drawable-xxhdpi/ic_programme.png
  23. 2 2
      app/src/main/res/layout/fragment_last_played.xml
  24. 8 6
      app/src/main/res/layout/fragment_nowplaying.xml
  25. 45 0
      app/src/main/res/layout/fragment_programme.xml
  26. 19 0
      app/src/main/res/layout/fragment_programme_day.xml
  27. 4 1
      app/src/main/res/layout/news_view.xml
  28. 80 0
      app/src/main/res/layout/programme_view.xml
  29. 1 1
      app/src/main/res/layout/song_view.xml
  30. 5 0
      app/src/main/res/menu/bottom_nav_menu.xml
  31. 14 0
      app/src/main/res/navigation/navigation_programme.xml
  32. 0 9
      app/src/main/res/values/arrays.xml
  33. 5 5
      app/src/main/res/values/colors.xml
  34. 9 2
      app/src/main/res/values/strings.xml
  35. 1 3
      app/src/main/res/xml/alarm_preferences.xml

+ 66 - 0
app/src/main/assets/planning_example.json View File

1
+{
2
+  "planning": [
3
+    {
4
+      "title": "Musique Classique",
5
+      "periodicity": "1111111",
6
+      "hour_begin": "6:00",
7
+      "hour_end": "7:00"
8
+    },
9
+    {
10
+      "title": "Jeux Vidéos Matin",
11
+      "periodicity": "1111100",
12
+      "hour_begin": "09:00",
13
+      "hour_end": "10:30"
14
+    },
15
+    {
16
+      "title": "Anime Matin",
17
+      "periodicity": "1111100",
18
+      "hour_begin": "10:30",
19
+      "hour_end": "12:00"
20
+    },
21
+    {
22
+      "title": "Jeux Vidéos Aprem'",
23
+      "periodicity": "1111111",
24
+      "hour_begin": "14:00",
25
+      "hour_end": "15:30"
26
+    },
27
+    {
28
+      "title": "Anime Aprem'",
29
+      "periodicity": "1111111",
30
+      "hour_begin": "15:30",
31
+      "hour_end": "17:00"
32
+    },
33
+    {
34
+      "title": "Jeux Vidéos Soirée",
35
+      "periodicity": "1111100",
36
+      "hour_begin": "19:00",
37
+      "hour_end": "20:00"
38
+    },
39
+    {
40
+      "title": "Anime Soirée",
41
+      "periodicity": "1111100",
42
+      "hour_begin": "20:00",
43
+      "hour_end": "21:00"
44
+    },
45
+    {
46
+      "title": "Anime Nuit",
47
+      "periodicity": "1111111",
48
+      "hour_begin": "00:00",
49
+      "hour_end": "01:00"
50
+    },
51
+    {
52
+      "title": "Jeux Vidéos Nuit",
53
+      "periodicity": "1111111",
54
+      "hour_begin": "01:00",
55
+      "hour_end": "02:00"
56
+    },
57
+    {
58
+      "title": "Programmation nocturne",
59
+      "periodicity": "1111111",
60
+      "hour_begin": "02:00",
61
+      "hour_end": "06:00"
62
+    }
63
+  ],
64
+  "regular_programme" : "Programmation habituelle",
65
+  "timezone": "GMT+1"
66
+}

+ 1 - 1
app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt View File

20
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
20
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
21
         }
21
         }
22
 
22
 
23
-        if (arg1.getStringExtra("action") == "fr.forum_thalie.tsumugi.${Actions.PLAY_OR_FALLBACK.name}" )
23
+        if (arg1.getStringExtra("action") == "$tag.${Actions.PLAY_OR_FALLBACK.name}" )
24
         {
24
         {
25
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
25
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
26
             if (PlayerStore.instance.streamerName.value.isNullOrBlank())
26
             if (PlayerStore.instance.streamerName.value.isNullOrBlank())

+ 11 - 3
app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt View File

16
 import java.util.Timer
16
 import java.util.Timer
17
 import android.view.MenuItem
17
 import android.view.MenuItem
18
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
18
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
19
+import fr.forum_thalie.tsumugi.planning.Planning
19
 
20
 
20
 
21
 
21
 /* Log to file import
22
 /* Log to file import
37
         val bottomNavigationView : BottomNavigationView = findViewById(R.id.bottom_nav)
38
         val bottomNavigationView : BottomNavigationView = findViewById(R.id.bottom_nav)
38
 
39
 
39
         //val navGraphIds = listOf(R.navigation.home, R.navigation.list, R.navigation.form)
40
         //val navGraphIds = listOf(R.navigation.home, R.navigation.list, R.navigation.form)
40
-        val navGraphIds = listOf(R.navigation.navigation_nowplaying, R.navigation.navigation_songs,
41
-            R.navigation.navigation_news)
41
+        val navGraphIds = listOf(
42
+                R.navigation.navigation_nowplaying,
43
+                R.navigation.navigation_songs,
44
+                R.navigation.navigation_news,
45
+                R.navigation.navigation_programme)
42
 
46
 
43
         // Setup the bottom navigation view with a list of navigation graphs
47
         // Setup the bottom navigation view with a list of navigation graphs
44
         val controller = bottomNavigationView.setupWithNavController(
48
         val controller = bottomNavigationView.setupWithNavController(
118
         RadioAlarm.instance.setNextAlarm(c = this) // this checks the preferenceStore before actually setting an alarm, don't worry.
122
         RadioAlarm.instance.setNextAlarm(c = this) // this checks the preferenceStore before actually setting an alarm, don't worry.
119
 
123
 
120
         // initialize programmatically accessible colors
124
         // initialize programmatically accessible colors
121
-        colorBlue = ResourcesCompat.getColor(resources, R.color.bluereq, null)
125
+        colorBlue = ResourcesCompat.getColor(resources, R.color.rblue, null)
122
         colorWhited = ResourcesCompat.getColor(resources, R.color.whited, null)
126
         colorWhited = ResourcesCompat.getColor(resources, R.color.whited, null)
123
         colorGreenList = (ResourcesCompat.getColorStateList(resources, R.color.button_green, null))
127
         colorGreenList = (ResourcesCompat.getColorStateList(resources, R.color.button_green, null))
124
         colorRedList = (ResourcesCompat.getColorStateList(resources, R.color.button_red, null))
128
         colorRedList = (ResourcesCompat.getColorStateList(resources, R.color.button_red, null))
125
         colorGreenListCompat = (ResourcesCompat.getColorStateList(resources, R.color.button_green_compat, null))
129
         colorGreenListCompat = (ResourcesCompat.getColorStateList(resources, R.color.button_green_compat, null))
130
+        colorAccent = (ResourcesCompat.getColor(resources, R.color.colorAccent, null))
126
 
131
 
127
         // Post-UI Launch
132
         // Post-UI Launch
128
         if (PlayerStore.instance.isInitialized)
133
         if (PlayerStore.instance.isInitialized)
155
             isTimerStarted = true
160
             isTimerStarted = true
156
         }
161
         }
157
 
162
 
163
+        // fetch program
164
+        Planning.instance.parseUrl(/* getString(R.string.planning_url) */ context = this)
165
+
158
         // initialize the UI
166
         // initialize the UI
159
         setTheme(R.style.AppTheme)
167
         setTheme(R.style.AppTheme)
160
         setContentView(R.layout.activity_main)
168
         setContentView(R.layout.activity_main)

+ 2 - 0
app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt View File

34
 import com.google.android.exoplayer2.metadata.icy.*
34
 import com.google.android.exoplayer2.metadata.icy.*
35
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
35
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
36
 import fr.forum_thalie.tsumugi.alarm.RadioSleeper
36
 import fr.forum_thalie.tsumugi.alarm.RadioSleeper
37
+import fr.forum_thalie.tsumugi.planning.Planning
37
 import fr.forum_thalie.tsumugi.playerstore.PlayerStore
38
 import fr.forum_thalie.tsumugi.playerstore.PlayerStore
38
 import java.util.*
39
 import java.util.*
39
 import kotlin.math.exp
40
 import kotlin.math.exp
129
             PlayerStore.instance.updateLp()
130
             PlayerStore.instance.updateLp()
130
         }
131
         }
131
         nowPlayingNotification.update(this)
132
         nowPlayingNotification.update(this)
133
+        Planning.instance.checkProgramme()
132
     }
134
     }
133
 
135
 
134
     private val streamerPictureObserver = Observer<Bitmap> {
136
     private val streamerPictureObserver = Observer<Bitmap> {

+ 15 - 2
app/src/main/java/fr/forum_thalie/tsumugi/Values.kt View File

2
 
2
 
3
 import android.content.SharedPreferences
3
 import android.content.SharedPreferences
4
 import android.content.res.ColorStateList
4
 import android.content.res.ColorStateList
5
+import kotlin.collections.ArrayList
5
 
6
 
6
 const val tag = "fr.forum_thalie.tsumugi"
7
 const val tag = "fr.forum_thalie.tsumugi"
7
-const val noConnectionValue = "No connection"
8
+const val noConnectionValue = "Arrêté."
8
 const val streamDownValue = "Tsumugi est HS !"
9
 const val streamDownValue = "Tsumugi est HS !"
10
+val weekdaysArray : Array<String> = arrayOf( "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche")
11
+
12
+val weekdays = ArrayList<String>().apply { weekdaysArray.forEach { add(it) } }
13
+val weekdaysSundayFirst = ArrayList<String>().apply {
14
+    weekdays.forEach {
15
+        add(it)
16
+    }
17
+    val lastDay = last()
18
+    removeAt(size - 1)
19
+    add(0, lastDay)
20
+}
21
+
9
 var colorBlue: Int = 0
22
 var colorBlue: Int = 0
10
 var colorWhited: Int = 0
23
 var colorWhited: Int = 0
24
+var colorAccent : Int = 0
11
 var colorGreenList: ColorStateList? = ColorStateList.valueOf(0)
25
 var colorGreenList: ColorStateList? = ColorStateList.valueOf(0)
12
 var colorRedList: ColorStateList? = ColorStateList.valueOf(0)
26
 var colorRedList: ColorStateList? = ColorStateList.valueOf(0)
13
 var colorGreenListCompat : ColorStateList? = ColorStateList.valueOf(0)
27
 var colorGreenListCompat : ColorStateList? = ColorStateList.valueOf(0)
14
-
15
 lateinit var preferenceStore : SharedPreferences
28
 lateinit var preferenceStore : SharedPreferences

+ 4 - 3
app/src/main/java/fr/forum_thalie/tsumugi/alarm/RadioAlarm.kt View File

10
 import androidx.preference.PreferenceManager
10
 import androidx.preference.PreferenceManager
11
 import fr.forum_thalie.tsumugi.*
11
 import fr.forum_thalie.tsumugi.*
12
 import java.util.*
12
 import java.util.*
13
+import kotlin.collections.ArrayList
13
 
14
 
14
 class RadioAlarm {
15
 class RadioAlarm {
15
 
16
 
38
 
39
 
39
         val alarmManager = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
40
         val alarmManager = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
40
         alarmIntent = Intent(c, BootBroadcastReceiver::class.java).let { intent ->
41
         alarmIntent = Intent(c, BootBroadcastReceiver::class.java).let { intent ->
41
-            intent.putExtra("action", "fr.forum_thalie.tsumugi.${Actions.PLAY_OR_FALLBACK.name}")
42
+            intent.putExtra("action", "$tag.${Actions.PLAY_OR_FALLBACK.name}")
42
             PendingIntent.getBroadcast(c, 0, intent, 0)
43
             PendingIntent.getBroadcast(c, 0, intent, 0)
43
         }
44
         }
44
         val showIntent = Intent(c, ParametersActivity::class.java).let { intent ->
45
         val showIntent = Intent(c, ParametersActivity::class.java).let { intent ->
60
         val hourOfDay = time / 60 //time is in minutes
61
         val hourOfDay = time / 60 //time is in minutes
61
         val minute = time % 60
62
         val minute = time % 60
62
 
63
 
63
-        val fullWeekOrdered = arrayListOf("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
64
+        val fullWeekOrdered = weekdaysSundayFirst //Sunday --> Saturday
64
         val selectedDays = arrayListOf<Int>()
65
         val selectedDays = arrayListOf<Int>()
65
         for (item in fullWeekOrdered)
66
         for (item in fullWeekOrdered)
66
         {
67
         {
100
         {
101
         {
101
             val alarmManager = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
102
             val alarmManager = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
102
             alarmIntent = Intent(c, BootBroadcastReceiver::class.java).let { intent ->
103
             alarmIntent = Intent(c, BootBroadcastReceiver::class.java).let { intent ->
103
-                intent.putExtra("action", "fr.forum_thalie.tsumugi.${Actions.PLAY_OR_FALLBACK.name}")
104
+                intent.putExtra("action", "$tag.${Actions.PLAY_OR_FALLBACK.name}")
104
                 PendingIntent.getBroadcast(c, 0, intent, 0)
105
                 PendingIntent.getBroadcast(c, 0, intent, 0)
105
             }
106
             }
106
             val showIntent = Intent(c, ParametersActivity::class.java).let { intent ->
107
             val showIntent = Intent(c, ParametersActivity::class.java).let { intent ->

+ 109 - 0
app/src/main/java/fr/forum_thalie/tsumugi/planning/Planning.kt View File

1
+package fr.forum_thalie.tsumugi.planning
2
+
3
+import android.content.Context
4
+import androidx.lifecycle.MutableLiveData
5
+import fr.forum_thalie.tsumugi.Async
6
+import fr.forum_thalie.tsumugi.R
7
+import fr.forum_thalie.tsumugi.weekdays
8
+import org.json.JSONObject
9
+import java.io.IOException
10
+import java.net.URL
11
+import java.util.*
12
+import kotlin.collections.ArrayList
13
+
14
+class Planning {
15
+
16
+    val programmes: ArrayList<Programme> = ArrayList()
17
+    private var regularProgramme: String? = null
18
+    val currentProgramme: MutableLiveData<String> = MutableLiveData()
19
+    val isDataFetched: MutableLiveData<Boolean> = MutableLiveData()
20
+
21
+    var timeZone: TimeZone = Calendar.getInstance().timeZone
22
+
23
+    private fun findCurrentProgramme(): String
24
+    {
25
+        programmes.forEach {
26
+            if (it.isCurrent())
27
+                return it.title
28
+        }
29
+        return regularProgramme ?: "—"
30
+    }
31
+
32
+    fun checkProgramme()
33
+    {
34
+        val newProgramme = findCurrentProgramme()
35
+        if (currentProgramme.value != newProgramme)
36
+            currentProgramme.value = newProgramme
37
+    }
38
+
39
+    fun parseUrl(url: String? = null, context: Context? = null)
40
+    {
41
+        val scrape : (Any?) -> String = {
42
+            if (url.isNullOrEmpty() && context != null)
43
+            {
44
+                val json: String
45
+                try {
46
+                    val inputStream = context.assets.open("planning_example.json")
47
+                    val size = inputStream.available()
48
+                    val buffer = ByteArray(size)
49
+                    inputStream.use { it.read(buffer) }
50
+                    json = String(buffer)
51
+                    json
52
+                } catch (ioException: IOException) {
53
+                    ioException.printStackTrace()
54
+                    ""
55
+                }
56
+            }
57
+            else
58
+                URL(url).readText()
59
+
60
+        }
61
+        val post : (parameter: Any?) -> Unit = {
62
+            val result = JSONObject(it as String)
63
+            if (result.has("planning"))
64
+            {
65
+                val programList = result.getJSONArray("planning")
66
+                for (i in 0 until programList.length())
67
+                {
68
+                    val item = programList[i] as JSONObject
69
+                    var periodicityDec = item.getInt("periodicity")
70
+                    var periodicity = 0b0000000
71
+                    var po = 1000000
72
+                    for (j in 0 until weekdays.size)
73
+                    {
74
+                        if (periodicityDec / (po) > 0)
75
+                        {
76
+                            periodicityDec -= po
77
+                            periodicity += 1 shl (weekdays.size-1 - j)
78
+                        }
79
+                        po /= 10
80
+                    }
81
+                    val hourBeginS = item.getString("hour_begin").split(":")
82
+                    val hourBegin = hourBeginS.first().toInt()*60 + hourBeginS.last().toInt()
83
+                    val hourEndS = item.getString("hour_end").split(":")
84
+                    val hourEnd = hourEndS.first().toInt()* 60 + hourEndS.last().toInt()
85
+                    val title = item.getString("title")
86
+                    programmes.add(Programme(title, periodicity, hourBegin, hourEnd))
87
+                }
88
+            }
89
+            regularProgramme = if (result.has("regular_programme"))
90
+                result.getString("regular_programme")
91
+            else
92
+                context?.getString(R.string.regular_programme)
93
+
94
+            timeZone = if (result.has("timezone"))
95
+                TimeZone.getTimeZone(result.getString("timezone"))
96
+            else
97
+                Calendar.getInstance().timeZone
98
+
99
+            isDataFetched.value = true
100
+        }
101
+        Async(scrape, post)
102
+    }
103
+
104
+    companion object {
105
+        val instance by lazy {
106
+            Planning()
107
+        }
108
+    }
109
+}

+ 92 - 0
app/src/main/java/fr/forum_thalie/tsumugi/planning/Programme.kt View File

1
+package fr.forum_thalie.tsumugi.planning
2
+
3
+import android.util.Log
4
+import fr.forum_thalie.tsumugi.tag
5
+import fr.forum_thalie.tsumugi.weekdays
6
+import java.util.*
7
+import kotlin.collections.ArrayList
8
+
9
+class Programme (val title: String, private val periodicity: Int, private val hourBegin: Int, private val hourEnd: Int) {
10
+
11
+    fun isThisDay(day: Int): Boolean {
12
+
13
+        // 0 (Monday) to 5 (Saturday) + 6 (Sunday)
14
+
15
+        // this translates to "true" when:
16
+        // - the currentDay is flagged in the "periodicity" bit array
17
+        // OR
18
+        // - Yesterday is flagged in the "periodicity" bit array AND the program does span over 2 days (night programs).
19
+        // We'll check a after this whether the current hour is within the span.
20
+        return (((0b1000000 shr day) and (periodicity)) != 0)
21
+    }
22
+
23
+    fun isCurrent(): Boolean {
24
+        val now = Calendar.getInstance(Planning.instance.timeZone)
25
+        val currentDay =
26
+            if (now.get(Calendar.DAY_OF_WEEK) - 1 == 0) 6 else now.get(Calendar.DAY_OF_WEEK) - 2
27
+        // 0 (Monday) to 5 (Saturday) + 6 (Sunday)
28
+
29
+        // this translates to "true" when:
30
+        // - the currentDay is flagged in the "periodicity" bit array
31
+        // OR
32
+        // - Yesterday is flagged in the "periodicity" bit array AND the program does span over 2 days (night programs).
33
+        // We'll check a after this whether the current hour is within the span.
34
+        val isToday: Boolean = (((0b1000000 shr currentDay) and (periodicity)) != 0)
35
+        val isSpanningOverNight =
36
+            (((0b1000000 shr ((currentDay - 1) % 7) and (periodicity)) != 0) && hourEnd < hourBegin)
37
+
38
+        Log.d(tag, "$title is today: $isToday or spanning $isSpanningOverNight")
39
+        // shr = shift-right. It's a binary mask.
40
+
41
+        // if the program started yesterday, and spanned over night, it means that there could be a chance that it's still active.
42
+        // we only need to check if the end time has been reached.
43
+        if (isSpanningOverNight) {
44
+            return (now.get(Calendar.HOUR_OF_DAY) * 60 + now.get(Calendar.MINUTE) < hourEnd)
45
+        }
46
+
47
+        // if the program is today, we need to check if we're in the hour span.
48
+        if (isToday) {
49
+            val hasBegun =
50
+                (now.get(Calendar.HOUR_OF_DAY) * 60 + now.get(Calendar.MINUTE) >= hourBegin)
51
+            val hasNotEnded =
52
+                (now.get(Calendar.HOUR_OF_DAY) * 60 + now.get(Calendar.MINUTE) < hourEnd) || hourEnd < hourBegin
53
+            return (hasBegun && hasNotEnded)
54
+        }
55
+        return false
56
+    }
57
+
58
+    override fun toString(): String {
59
+        return "Title: $title, time info (periodicity, begin, end): $periodicity, $hourBegin, $hourEnd"
60
+    }
61
+
62
+    private val offset1 = (Planning.instance.timeZone.getOffset(System.currentTimeMillis())) / (60*1000)
63
+    private val offset2 = (Calendar.getInstance().timeZone.getOffset(System.currentTimeMillis())) / (60*1000)
64
+
65
+    fun begin(): String {
66
+        val hourBeginTZCorrect = hourBegin // + offset1 - offset2
67
+        return "%02d:%02d".format(hourBeginTZCorrect/60, hourBeginTZCorrect%60)
68
+    }
69
+
70
+    fun end(): String {
71
+        val hourEndTZCorrect = hourEnd // + offset1 - offset2
72
+        return "%02d:%02d".format(hourEndTZCorrect/60, hourEndTZCorrect%60)
73
+    }
74
+
75
+    /*
76
+    fun days(): String {
77
+        val res = ArrayList<String>()
78
+        for (i in 0 until weekdays.size) {
79
+            if (((0b1000000 shr i) and (periodicity)) != 0)
80
+            {
81
+                res.add(weekdays[i])
82
+            }
83
+        }
84
+        return res.toString().drop(1).dropLast(1) //  drop '[' and ']'
85
+    }
86
+
87
+     */
88
+
89
+    init {
90
+        Log.d(tag, this.toString())
91
+    }
92
+}

+ 13 - 13
app/src/main/java/fr/forum_thalie/tsumugi/preferences/AlarmFragment.kt View File

5
 import android.util.Log
5
 import android.util.Log
6
 import androidx.core.content.edit
6
 import androidx.core.content.edit
7
 import androidx.preference.*
7
 import androidx.preference.*
8
+import fr.forum_thalie.tsumugi.*
8
 import fr.forum_thalie.tsumugi.R
9
 import fr.forum_thalie.tsumugi.R
9
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
10
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
10
 import java.util.*
11
 import java.util.*
18
         val alarmDays = findPreference<MultiSelectListPreference>("alarmDays")
19
         val alarmDays = findPreference<MultiSelectListPreference>("alarmDays")
19
         val snoozeDuration = findPreference<ListPreference>("snoozeDuration")
20
         val snoozeDuration = findPreference<ListPreference>("snoozeDuration")
20
 
21
 
22
+        alarmDays?.entries = weekdaysArray
23
+        alarmDays?.entryValues = weekdaysArray
24
+        alarmDays?.setDefaultValue(weekdaysArray)
21
 
25
 
22
         fun updateIsWakingUpSummary(preference: SwitchPreferenceCompat?, newValue: Boolean? = true,  forceTime: Int? = null, forceDays: Set<String>? = null)
26
         fun updateIsWakingUpSummary(preference: SwitchPreferenceCompat?, newValue: Boolean? = true,  forceTime: Int? = null, forceDays: Set<String>? = null)
23
         {
27
         {
26
             calendar.timeInMillis = dateLong
30
             calendar.timeInMillis = dateLong
27
             if (newValue == true && calendar.timeInMillis > 0)
31
             if (newValue == true && calendar.timeInMillis > 0)
28
             {
32
             {
29
-                val fullWeekOrdered = arrayListOf("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
30
-                preference?.summary = "Next alarm on ${fullWeekOrdered[calendar.get(Calendar.DAY_OF_WEEK) - 1]} " +
31
-                        "at ${if (calendar.get(Calendar.HOUR_OF_DAY) < 10) "0" else ""}${calendar.get(Calendar.HOUR_OF_DAY)}" +
32
-                        ":${if (calendar.get(Calendar.MINUTE) < 10) "0" else ""}${calendar.get(Calendar.MINUTE)}"
33
+                val fullWeekOrdered = weekdaysSundayFirst
34
+                preference?.summary = getString(R.string.next_alarm) + " ${fullWeekOrdered[calendar.get(Calendar.DAY_OF_WEEK) - 1]} ${if (calendar.get(Calendar.HOUR_OF_DAY) < 10) "0" else ""}${calendar.get(Calendar.HOUR_OF_DAY)}:${if (calendar.get(Calendar.MINUTE) < 10) "0" else ""}${calendar.get(Calendar.MINUTE)}"
33
             } else if (newValue == true)
35
             } else if (newValue == true)
34
-                preference?.summary = "Select at least one day"
36
+                preference?.summary = getString(R.string.select_one_day)
35
             else
37
             else
36
             {
38
             {
37
-                preference?.summary = "No alarm set"
39
+                preference?.summary = getString(R.string.no_alarm_set)
38
             }
40
             }
39
         }
41
         }
40
 
42
 
76
 
78
 
77
         fun updateDays(preference : MultiSelectListPreference?, newValue : Set<String>?)
79
         fun updateDays(preference : MultiSelectListPreference?, newValue : Set<String>?)
78
         {
80
         {
79
-            Log.d(tag, newValue.toString())
80
-            val listOfDays : String
81
-            val fullWeek = context!!.resources.getStringArray(R.array.weekdays).toSet()
82
-            val workingWeek = context!!.resources.getStringArray(R.array.weekdays).toSet().minusElement("Saturday").minusElement("Sunday")
83
-            listOfDays = when (newValue) {
81
+            val fullWeek = weekdays.toSet()
82
+            val workingWeek = weekdays.toSet().minusElement(weekdays.last()).minusElement(weekdays.elementAt(weekdays.size - 2))
83
+            val listOfDays = when (newValue) {
84
                 fullWeek -> context!!.getString(R.string.every_day)
84
                 fullWeek -> context!!.getString(R.string.every_day)
85
                 workingWeek -> context!!.getString(R.string.working_days)
85
                 workingWeek -> context!!.getString(R.string.working_days)
86
                 else -> {
86
                 else -> {
87
                     // build ORDERED LIST of days... I don't know why the original one is in shambles!!
87
                     // build ORDERED LIST of days... I don't know why the original one is in shambles!!
88
-                    val fullWeekOrdered = arrayListOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
89
-                    val selectedDays = arrayListOf<String>()
88
+                    val fullWeekOrdered = weekdays
89
+                    val selectedDays = ArrayList<String>()
90
                     for (item in fullWeekOrdered) {
90
                     for (item in fullWeekOrdered) {
91
                         if (newValue!!.contains(item))
91
                         if (newValue!!.contains(item))
92
                             selectedDays.add(item)
92
                             selectedDays.add(item)

app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsPagerAdapter.kt → app/src/main/java/fr/forum_thalie/tsumugi/ui/APagerAdapter.kt View File

1
-package fr.forum_thalie.tsumugi.ui.songs
1
+package fr.forum_thalie.tsumugi.ui
2
 
2
 
3
 import androidx.fragment.app.Fragment
3
 import androidx.fragment.app.Fragment
4
 import androidx.fragment.app.FragmentPagerAdapter
4
 import androidx.fragment.app.FragmentPagerAdapter
5
 import androidx.fragment.app.FragmentManager
5
 import androidx.fragment.app.FragmentManager
6
 
6
 
7
 
7
 
8
-class SongsPagerAdapter(f: FragmentManager, t: Int) : FragmentPagerAdapter(f, t){
8
+class APagerAdapter(f: FragmentManager, t: Int) : FragmentPagerAdapter(f, t){
9
 
9
 
10
     private val fragmentList = ArrayList<Fragment>()
10
     private val fragmentList = ArrayList<Fragment>()
11
     private val fragmentTitleList = ArrayList<String>()
11
     private val fragmentTitleList = ArrayList<String>()

+ 11 - 4
app/src/main/java/fr/forum_thalie/tsumugi/ui/nowplaying/NowPlayingFragment.kt View File

21
 import com.google.android.material.snackbar.Snackbar
21
 import com.google.android.material.snackbar.Snackbar
22
 import fr.forum_thalie.tsumugi.*
22
 import fr.forum_thalie.tsumugi.*
23
 import fr.forum_thalie.tsumugi.alarm.RadioSleeper
23
 import fr.forum_thalie.tsumugi.alarm.RadioSleeper
24
+import fr.forum_thalie.tsumugi.planning.Planning
24
 import fr.forum_thalie.tsumugi.playerstore.PlayerStore
25
 import fr.forum_thalie.tsumugi.playerstore.PlayerStore
25
 import fr.forum_thalie.tsumugi.playerstore.Song
26
 import fr.forum_thalie.tsumugi.playerstore.Song
27
+import kotlinx.android.synthetic.main.fragment_nowplaying.*
26
 
28
 
27
 
29
 
28
 class NowPlayingFragment : Fragment() {
30
 class NowPlayingFragment : Fragment() {
51
         val streamerPictureImageView: ImageView = root.findViewById(R.id.streamerPicture)
53
         val streamerPictureImageView: ImageView = root.findViewById(R.id.streamerPicture)
52
 
54
 
53
         // Note: these values are not used in the generic app, but if you want to, you can use them.
55
         // Note: these values are not used in the generic app, but if you want to, you can use them.
56
+        val songTitleNextText: TextView = root.findViewById(R.id.text_song_title_next)
57
+        //val songArtistNextText: TextView = root.findViewById(R.id.text_song_artist_next)
58
+
54
         /*
59
         /*
55
         val streamerNameText : TextView = root.findViewById(R.id.streamerName)
60
         val streamerNameText : TextView = root.findViewById(R.id.streamerName)
56
-        val songTitleNextText: TextView = root.findViewById(R.id.text_song_title_next)
57
-        val songArtistNextText: TextView = root.findViewById(R.id.text_song_artist_next)
58
         val listenersText : TextView = root.findViewById(R.id.listenersCount)
61
         val listenersText : TextView = root.findViewById(R.id.listenersCount)
59
          */
62
          */
60
 
63
 
86
 
89
 
87
 
90
 
88
         PlayerStore.instance.currentSong.title.observe(viewLifecycleOwner, Observer {
91
         PlayerStore.instance.currentSong.title.observe(viewLifecycleOwner, Observer {
89
-                songTitleText.text = it
92
+            songTitleText.text = it
93
+        })
94
+
95
+        Planning.instance.currentProgramme.observe(viewLifecycleOwner, Observer {
96
+            songTitleNextText.text = it
90
         })
97
         })
91
 
98
 
92
         PlayerStore.instance.currentSong.artist.observe(viewLifecycleOwner, Observer {
99
         PlayerStore.instance.currentSong.artist.observe(viewLifecycleOwner, Observer {
93
-                songArtistText.text = it
100
+            songArtistText.text = it
94
         })
101
         })
95
 
102
 
96
         PlayerStore.instance.playbackState.observe(viewLifecycleOwner, Observer {
103
         PlayerStore.instance.playbackState.observe(viewLifecycleOwner, Observer {

+ 72 - 0
app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeAdapter.kt View File

1
+package fr.forum_thalie.tsumugi.ui.programme
2
+
3
+import android.annotation.SuppressLint
4
+import android.util.TypedValue
5
+import android.view.LayoutInflater
6
+import android.view.ViewGroup
7
+import android.widget.TextView
8
+import androidx.constraintlayout.widget.ConstraintLayout
9
+import androidx.core.widget.TextViewCompat
10
+import androidx.recyclerview.widget.RecyclerView
11
+import fr.forum_thalie.tsumugi.*
12
+import fr.forum_thalie.tsumugi.planning.Planning
13
+import fr.forum_thalie.tsumugi.planning.Programme
14
+import java.util.*
15
+import kotlin.collections.ArrayList
16
+
17
+class ProgrammeAdapter(private val dataSet: ArrayList<Programme>, private val day: String
18
+    /*,
19
+    context: Context,
20
+    resource: Int,
21
+    objects: ArrayList<Programme>*/
22
+) : RecyclerView.Adapter<ProgrammeAdapter.MyViewHolder>() /*ArrayAdapter<Song>(context, resource, objects)*/ {
23
+
24
+    class MyViewHolder(view: ConstraintLayout) : RecyclerView.ViewHolder(view)
25
+
26
+    // Create new views (invoked by the layout manager)
27
+    override fun onCreateViewHolder(parent: ViewGroup,
28
+                                    viewType: Int): MyViewHolder {
29
+        // create a new view
30
+        val view = LayoutInflater.from(parent.context)
31
+            .inflate(R.layout.programme_view, parent, false) as ConstraintLayout
32
+        return MyViewHolder(
33
+            view
34
+        )
35
+    }
36
+
37
+    // Replace the contents of a view (invoked by the layout manager)
38
+    @SuppressLint("SetTextI18n")
39
+    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
40
+        val programmeStart = holder.itemView.findViewById<TextView>(R.id.programme_start)
41
+        val programmeEnd = holder.itemView.findViewById<TextView>(R.id.programme_end)
42
+        val programmeName = holder.itemView.findViewById<TextView>(R.id.programme_name)
43
+
44
+        programmeStart.text = dataSet[position].begin()
45
+        programmeName.text = dataSet[position].title
46
+        programmeEnd.text = dataSet[position].end()
47
+
48
+        val color =  if (dataSet[position].isCurrent() && (Calendar.getInstance(Planning.instance.timeZone).get(Calendar.DAY_OF_WEEK) - 1 == weekdaysSundayFirst.indexOf(day)))
49
+            colorAccent
50
+        else
51
+            colorWhited
52
+
53
+        programmeStart.setTextColor(color)
54
+        programmeEnd.setTextColor(color)
55
+        programmeName.setTextColor(color)
56
+
57
+        //TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
58
+        //    programmeName,8, 16, 2, TypedValue.COMPLEX_UNIT_SP)
59
+    }
60
+
61
+    // Return the size of your dataset (invoked by the layout manager)
62
+    override fun getItemCount() = dataSet.size
63
+
64
+
65
+    /*
66
+    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
67
+        // create a new view
68
+        val view = LayoutInflater.from(parent.context)
69
+            .inflate(R.layout.song_view, parent, false) as ConstraintLayout
70
+    }
71
+    */
72
+}

+ 75 - 0
app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeDayFragment.kt View File

1
+package fr.forum_thalie.tsumugi.ui.programme
2
+
3
+import android.os.Bundle
4
+import android.view.LayoutInflater
5
+import android.view.View
6
+import android.view.ViewGroup
7
+import androidx.fragment.app.Fragment
8
+import androidx.lifecycle.Observer
9
+import androidx.lifecycle.ViewModelProviders
10
+import androidx.recyclerview.widget.LinearLayoutManager
11
+import androidx.recyclerview.widget.RecyclerView
12
+import fr.forum_thalie.tsumugi.R
13
+import fr.forum_thalie.tsumugi.planning.Planning
14
+import fr.forum_thalie.tsumugi.planning.Programme
15
+import fr.forum_thalie.tsumugi.weekdays
16
+
17
+
18
+class ProgrammeDayFragment : Fragment() {
19
+
20
+    private lateinit var recyclerView: RecyclerView
21
+    private lateinit var viewAdapter: RecyclerView.Adapter<*>
22
+    private lateinit var viewManager: RecyclerView.LayoutManager
23
+    private val programmeOfTheDay: ArrayList<Programme> = ArrayList()
24
+
25
+
26
+    override fun onCreateView(
27
+        inflater: LayoutInflater, container: ViewGroup?,
28
+        savedInstanceState: Bundle?
29
+    ): View? {
30
+        // Inflate the layout for this fragment
31
+        val viewModel: ProgrammeDayViewModel = ViewModelProviders.of(this).get(ProgrammeDayViewModel::class.java)
32
+        viewModel.day = arguments?.getString("day") ?: ""
33
+
34
+        val root = inflater.inflate(R.layout.fragment_programme_day, container, false)
35
+        Planning.instance.programmes.forEach {
36
+            if (it.isThisDay(day = weekdays.indexOf(viewModel.day)))
37
+                programmeOfTheDay.add(it)
38
+        }
39
+        viewManager = LinearLayoutManager(context)
40
+        viewAdapter =
41
+            ProgrammeAdapter(programmeOfTheDay, viewModel.day)
42
+        recyclerView = root.findViewById<RecyclerView>(R.id.programme_recycler).apply {
43
+            // use this setting to improve performance if you know that changes
44
+            // in content do not change the layout size of the RecyclerView
45
+            setHasFixedSize(true)
46
+            layoutManager = viewManager
47
+            adapter = viewAdapter
48
+        }
49
+        Planning.instance.currentProgramme.observe(viewLifecycleOwner,  Observer<String> {
50
+            programmeOfTheDay.clear()
51
+            Planning.instance.programmes.forEach {
52
+                if (it.isThisDay(day = weekdays.indexOf(viewModel.day)))
53
+                    programmeOfTheDay.add(it)
54
+            }
55
+            viewAdapter.notifyDataSetChanged()
56
+        })
57
+
58
+        Planning.instance.isDataFetched.observe(viewLifecycleOwner, Observer<Boolean> {
59
+            viewAdapter.notifyDataSetChanged()
60
+        })
61
+        return root
62
+    }
63
+
64
+
65
+    companion object {
66
+        @JvmStatic
67
+        fun newInstance(day: String): ProgrammeDayFragment {
68
+            val args = Bundle()
69
+            args.putString("day", day)
70
+            val f = ProgrammeDayFragment()
71
+            f.arguments = args
72
+            return f
73
+        }
74
+    }
75
+}

+ 8 - 0
app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeDayViewModel.kt View File

1
+package fr.forum_thalie.tsumugi.ui.programme
2
+
3
+import androidx.lifecycle.ViewModel
4
+
5
+class ProgrammeDayViewModel: ViewModel() {
6
+    var day: String = ""
7
+
8
+}

+ 49 - 0
app/src/main/java/fr/forum_thalie/tsumugi/ui/programme/ProgrammeFragment.kt View File

1
+package fr.forum_thalie.tsumugi.ui.programme
2
+
3
+import android.os.Bundle
4
+import android.util.Log
5
+import android.view.LayoutInflater
6
+import android.view.View
7
+import android.view.ViewGroup
8
+import androidx.fragment.app.Fragment
9
+import androidx.fragment.app.FragmentPagerAdapter
10
+import androidx.viewpager.widget.ViewPager
11
+import com.google.android.material.tabs.TabLayout
12
+import fr.forum_thalie.tsumugi.R
13
+import fr.forum_thalie.tsumugi.planning.Planning
14
+import fr.forum_thalie.tsumugi.ui.APagerAdapter
15
+import fr.forum_thalie.tsumugi.weekdays
16
+import java.util.*
17
+
18
+class ProgrammeFragment : Fragment() {
19
+
20
+    private lateinit var adapter : APagerAdapter
21
+    private lateinit var root: View
22
+    private lateinit var viewPager: ViewPager
23
+
24
+    override fun onCreateView(
25
+        inflater: LayoutInflater,
26
+        container: ViewGroup?,
27
+        savedInstanceState: Bundle?
28
+    ): View? {
29
+
30
+        root = inflater.inflate(R.layout.fragment_programme, container, false)
31
+        viewPager = root.findViewById(R.id.dayTabPager)
32
+        adapter = APagerAdapter(childFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
33
+        // You can add more fragments to the adapter, to display more information (for example with R/a/dio, queue, request, faves...)
34
+        weekdays.forEach {
35
+            adapter.addFragment(ProgrammeDayFragment.newInstance(it), it)
36
+        }
37
+
38
+        viewPager.adapter = adapter
39
+        val todaySundayFirst = Calendar.getInstance(Planning.instance.timeZone).get(Calendar.DAY_OF_WEEK) - 1
40
+        viewPager.currentItem = (todaySundayFirst - 1)%7
41
+
42
+        val tabLayout : TabLayout = root.findViewById(R.id.dayTabLayout)
43
+        tabLayout.setupWithViewPager(viewPager)
44
+        Log.d(tag, "SongFragment view created")
45
+
46
+        return root
47
+    }
48
+
49
+}

+ 8 - 5
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt View File

10
 import androidx.viewpager.widget.ViewPager
10
 import androidx.viewpager.widget.ViewPager
11
 import com.google.android.material.tabs.TabLayout
11
 import com.google.android.material.tabs.TabLayout
12
 import fr.forum_thalie.tsumugi.R
12
 import fr.forum_thalie.tsumugi.R
13
+import fr.forum_thalie.tsumugi.ui.APagerAdapter
14
+import fr.forum_thalie.tsumugi.ui.programme.ProgrammeDayFragment
13
 import fr.forum_thalie.tsumugi.ui.songs.queuelp.LastPlayedFragment
15
 import fr.forum_thalie.tsumugi.ui.songs.queuelp.LastPlayedFragment
14
 
16
 
15
 class SongsFragment : Fragment() {
17
 class SongsFragment : Fragment() {
16
 
18
 
17
-    private lateinit var adapter : SongsPagerAdapter
19
+    private lateinit var adapter : APagerAdapter
18
     private lateinit var root: View
20
     private lateinit var root: View
19
     private lateinit var viewPager: ViewPager
21
     private lateinit var viewPager: ViewPager
20
 
22
 
26
 
28
 
27
         root = inflater.inflate(R.layout.fragment_songs, container, false)
29
         root = inflater.inflate(R.layout.fragment_songs, container, false)
28
         viewPager = root.findViewById(R.id.tabPager)
30
         viewPager = root.findViewById(R.id.tabPager)
29
-        adapter = SongsPagerAdapter(childFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
30
-        adapter.addFragment(LastPlayedFragment.newInstance(), "last played")
31
-
31
+        adapter = APagerAdapter(
32
+            childFragmentManager,
33
+            FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
34
+        )
35
+        adapter.addFragment(LastPlayedFragment.newInstance(), getString(R.string.lp))
32
         // You can add more fragments to the adapter, to display more information (for example with R/a/dio, queue, request, faves...)
36
         // You can add more fragments to the adapter, to display more information (for example with R/a/dio, queue, request, faves...)
33
 
37
 
34
         viewPager.adapter = adapter
38
         viewPager.adapter = adapter
37
         tabLayout.setupWithViewPager(viewPager)
41
         tabLayout.setupWithViewPager(viewPager)
38
         Log.d(tag, "SongFragment view created")
42
         Log.d(tag, "SongFragment view created")
39
 
43
 
40
-
41
         return root
44
         return root
42
     }
45
     }
43
 
46
 

+ 2 - 0
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/SongAdaptater.kt View File

2
 
2
 
3
 import android.annotation.SuppressLint
3
 import android.annotation.SuppressLint
4
 import android.graphics.Color
4
 import android.graphics.Color
5
+import android.util.Log
5
 import android.view.LayoutInflater
6
 import android.view.LayoutInflater
6
 import android.view.ViewGroup
7
 import android.view.ViewGroup
7
 import androidx.constraintlayout.widget.ConstraintLayout
8
 import androidx.constraintlayout.widget.ConstraintLayout
11
 import fr.forum_thalie.tsumugi.colorBlue
12
 import fr.forum_thalie.tsumugi.colorBlue
12
 import fr.forum_thalie.tsumugi.colorWhited
13
 import fr.forum_thalie.tsumugi.colorWhited
13
 import fr.forum_thalie.tsumugi.playerstore.Song
14
 import fr.forum_thalie.tsumugi.playerstore.Song
15
+import fr.forum_thalie.tsumugi.tag
14
 import kotlinx.android.synthetic.main.song_view.view.*
16
 import kotlinx.android.synthetic.main.song_view.view.*
15
 import kotlin.collections.ArrayList
17
 import kotlin.collections.ArrayList
16
 
18
 

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_programme.xml View File

1
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+    android:width="24dp"
3
+    android:height="24dp"
4
+    android:viewportWidth="24"
5
+    android:viewportHeight="24"
6
+    android:tint="#FFFFFF"
7
+    android:alpha="0.8">
8
+    <path
9
+        android:fillColor="#FF000000"
10
+        android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11zM7,10h5v5L7,15z"/>
11
+</vector>

BIN
app/src/main/res/drawable-hdpi/ic_programme.png View File


BIN
app/src/main/res/drawable-mdpi/ic_programme.png View File


BIN
app/src/main/res/drawable-xhdpi/ic_programme.png View File


BIN
app/src/main/res/drawable-xxhdpi/ic_programme.png View File


+ 2 - 2
app/src/main/res/layout/fragment_last_played.xml View File

1
 <?xml version="1.0" encoding="utf-8"?>
1
 <?xml version="1.0" encoding="utf-8"?>
2
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2
+<LinearLayout
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
3
     xmlns:tools="http://schemas.android.com/tools"
4
     xmlns:tools="http://schemas.android.com/tools"
4
     android:layout_width="match_parent"
5
     android:layout_width="match_parent"
5
     android:layout_height="match_parent"
6
     android:layout_height="match_parent"
6
     android:layout_margin="8dp"
7
     android:layout_margin="8dp"
7
     tools:context=".ui.songs.queuelp.LastPlayedFragment">
8
     tools:context=".ui.songs.queuelp.LastPlayedFragment">
8
 
9
 
9
-    <!-- TODO: Update blank fragment layout -->
10
     <androidx.recyclerview.widget.RecyclerView
10
     <androidx.recyclerview.widget.RecyclerView
11
         android:layout_width="match_parent"
11
         android:layout_width="match_parent"
12
         android:layout_height="match_parent"
12
         android:layout_height="match_parent"

+ 8 - 6
app/src/main/res/layout/fragment_nowplaying.xml View File

30
         <ImageView
30
         <ImageView
31
             android:id="@+id/streamerPicture"
31
             android:id="@+id/streamerPicture"
32
             android:layout_width="0dp"
32
             android:layout_width="0dp"
33
-            android:layout_height="0dp"
33
+            android:layout_height="wrap_content"
34
             android:layout_gravity="top|center_horizontal"
34
             android:layout_gravity="top|center_horizontal"
35
             android:adjustViewBounds="true"
35
             android:adjustViewBounds="true"
36
             android:contentDescription="dj-image"
36
             android:contentDescription="dj-image"
37
             android:scaleType="fitCenter"
37
             android:scaleType="fitCenter"
38
-            app:layout_constraintBottom_toTopOf="@id/imageLeftGuideline"
39
             app:layout_constraintEnd_toEndOf="parent"
38
             app:layout_constraintEnd_toEndOf="parent"
40
 
39
 
41
             app:layout_constraintStart_toStartOf="@id/imageGuideline"
40
             app:layout_constraintStart_toStartOf="@id/imageGuideline"
42
             app:layout_constraintTop_toTopOf="parent"
41
             app:layout_constraintTop_toTopOf="parent"
43
             app:srcCompat="@drawable/logo_roundsquare"
42
             app:srcCompat="@drawable/logo_roundsquare"
43
+            android:layout_margin="8dp"
44
             tools:ignore="HardcodedText" />
44
             tools:ignore="HardcodedText" />
45
+        <!--             app:layout_constraintBottom_toTopOf="@id/imageLeftGuideline" -->
45
 
46
 
46
         <androidx.constraintlayout.widget.Guideline
47
         <androidx.constraintlayout.widget.Guideline
47
             android:id="@+id/imageGuideline"
48
             android:id="@+id/imageGuideline"
97
             android:layout_width="wrap_content"
98
             android:layout_width="wrap_content"
98
             android:layout_height="wrap_content"
99
             android:layout_height="wrap_content"
99
             android:orientation="horizontal"
100
             android:orientation="horizontal"
100
-            app:layout_constraintGuide_percent="0.80" />
101
+            app:layout_constraintGuide_percent="0.95" />
101
 
102
 
102
         <TextView
103
         <TextView
103
             android:id="@+id/sleepInfo"
104
             android:id="@+id/sleepInfo"
173
                 android:orientation="vertical"
174
                 android:orientation="vertical"
174
                 android:layout_marginEnd="8dp"
175
                 android:layout_marginEnd="8dp"
175
                 android:layout_marginRight="8dp"
176
                 android:layout_marginRight="8dp"
176
-                android:visibility="gone"
177
+                android:visibility="visible"
177
                 >
178
                 >
178
 
179
 
179
                 <TextView
180
                 <TextView
183
                     android:layout_gravity="top"
184
                     android:layout_gravity="top"
184
                     android:text="@string/up_next"
185
                     android:text="@string/up_next"
185
                     android:textAlignment="center"
186
                     android:textAlignment="center"
186
-                    android:textColor="@color/whited"
187
+                    android:textColor="@color/whited3"
187
                     android:layout_marginTop="8dp"
188
                     android:layout_marginTop="8dp"
188
                     app:layout_constraintStart_toStartOf="parent"
189
                     app:layout_constraintStart_toStartOf="parent"
189
                     app:layout_constraintEnd_toEndOf="parent"
190
                     app:layout_constraintEnd_toEndOf="parent"
200
                     android:textColor="@color/whited3"
201
                     android:textColor="@color/whited3"
201
                     android:textSize="16sp"
202
                     android:textSize="16sp"
202
                     app:layout_constraintStart_toStartOf="parent"
203
                     app:layout_constraintStart_toStartOf="parent"
203
-                    app:layout_constraintTop_toBottomOf="@id/upNext" />
204
+                    app:layout_constraintTop_toBottomOf="@id/upNext"
205
+                    android:visibility="gone"/>
204
 
206
 
205
                 <TextView
207
                 <TextView
206
                     android:id="@+id/text_song_title_next"
208
                     android:id="@+id/text_song_title_next"

+ 45 - 0
app/src/main/res/layout/fragment_programme.xml View File

1
+<?xml version="1.0" encoding="utf-8"?>
2
+<androidx.appcompat.widget.LinearLayoutCompat
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
4
+    xmlns:app="http://schemas.android.com/apk/res-auto"
5
+    android:layout_width="match_parent"
6
+    android:layout_height="match_parent" >
7
+
8
+
9
+    <androidx.viewpager.widget.ViewPager
10
+        android:id="@+id/dayTabPager"
11
+        android:layout_width="match_parent"
12
+        android:layout_height="match_parent"
13
+        app:layout_constraintEnd_toEndOf="parent"
14
+        app:layout_constraintStart_toStartOf="parent"
15
+        app:layout_constraintTop_toTopOf="parent" >
16
+
17
+        <com.google.android.material.tabs.TabLayout
18
+            android:id="@+id/dayTabLayout"
19
+            android:layout_width="wrap_content"
20
+            android:layout_height="wrap_content"
21
+            app:tabMode="scrollable"
22
+            app:tabGravity="center"
23
+            app:tabIndicatorFullWidth="true"
24
+            android:paddingBottom="8dp"
25
+            >
26
+            <!--
27
+            <com.google.android.material.tabs.TabItem
28
+                android:layout_width="wrap_content"
29
+                android:layout_height="wrap_content"
30
+                android:text="Last played" />
31
+
32
+            <com.google.android.material.tabs.TabItem
33
+                android:layout_width="wrap_content"
34
+                android:layout_height="wrap_content"
35
+                android:text="Queue" />
36
+
37
+            <com.google.android.material.tabs.TabItem
38
+                android:layout_width="wrap_content"
39
+                android:layout_height="wrap_content"
40
+                android:text="Request" />
41
+            -->
42
+        </com.google.android.material.tabs.TabLayout>
43
+    </androidx.viewpager.widget.ViewPager>
44
+
45
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 19 - 0
app/src/main/res/layout/fragment_programme_day.xml View File

1
+<?xml version="1.0" encoding="utf-8"?>
2
+<LinearLayout
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
4
+    xmlns:tools="http://schemas.android.com/tools"
5
+    android:layout_width="match_parent"
6
+    android:layout_height="match_parent"
7
+    android:layout_margin="8dp"
8
+    tools:context=".ui.programme.ProgrammeDayFragment">
9
+
10
+    <androidx.recyclerview.widget.RecyclerView
11
+        android:layout_width="match_parent"
12
+        android:layout_height="match_parent"
13
+        android:id="@+id/programme_recycler"
14
+        android:scrollbars="vertical"
15
+        >
16
+
17
+    </androidx.recyclerview.widget.RecyclerView>
18
+
19
+</LinearLayout>

+ 4 - 1
app/src/main/res/layout/news_view.xml View File

4
     xmlns:app="http://schemas.android.com/apk/res-auto"
4
     xmlns:app="http://schemas.android.com/apk/res-auto"
5
     android:orientation="vertical" android:layout_width="match_parent"
5
     android:orientation="vertical" android:layout_width="match_parent"
6
     android:layout_height="wrap_content"
6
     android:layout_height="wrap_content"
7
-    android:layout_margin="16dp">
7
+    android:layout_marginTop="16dp"
8
+    android:layout_marginStart="16dp"
9
+    android:layout_marginEnd="16dp">
8
 
10
 
9
     <TextView
11
     <TextView
10
         android:id="@+id/news_title"
12
         android:id="@+id/news_title"
76
         android:id="@+id/divider"
78
         android:id="@+id/divider"
77
         android:layout_width="match_parent"
79
         android:layout_width="match_parent"
78
         android:layout_height="1dp"
80
         android:layout_height="1dp"
81
+        android:layout_marginTop="16dp"
79
         android:background="?android:attr/listDivider"
82
         android:background="?android:attr/listDivider"
80
         app:layout_constraintTop_toBottomOf="@id/news_text"
83
         app:layout_constraintTop_toBottomOf="@id/news_text"
81
         app:layout_constraintStart_toStartOf="parent"
84
         app:layout_constraintStart_toStartOf="parent"

+ 80 - 0
app/src/main/res/layout/programme_view.xml View File

1
+<?xml version="1.0" encoding="utf-8"?>
2
+<androidx.constraintlayout.widget.ConstraintLayout
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
4
+    xmlns:app="http://schemas.android.com/apk/res-auto"
5
+    android:orientation="vertical" android:layout_width="match_parent"
6
+    android:layout_height="wrap_content"
7
+    android:layout_marginStart="8dp"
8
+    android:layout_marginEnd="8dp"
9
+    app:layout_constraintVertical_chainStyle="packed"
10
+    >
11
+
12
+    <androidx.constraintlayout.widget.ConstraintLayout
13
+        android:id="@+id/hours"
14
+        android:layout_width="wrap_content"
15
+        android:layout_height="0dp"
16
+        app:layout_constraintTop_toTopOf="parent"
17
+        app:layout_constraintBottom_toBottomOf="parent"
18
+        app:layout_constraintStart_toStartOf="parent"
19
+        android:layout_marginTop="8dp"
20
+        android:layout_marginBottom="8dp"
21
+        app:layout_constraintVertical_chainStyle="packed"
22
+        >
23
+        <TextView
24
+            android:id="@+id/programme_start"
25
+            android:layout_width="wrap_content"
26
+            android:layout_height="wrap_content"
27
+            app:layout_constraintStart_toStartOf="parent"
28
+            app:layout_constraintTop_toTopOf="parent"
29
+            app:layout_constraintBottom_toTopOf="@id/programme_end"
30
+            android:gravity="start"
31
+            android:textColor="@color/whited"
32
+            app:layout_constraintVertical_chainStyle="packed"
33
+            />
34
+
35
+        <TextView
36
+            android:id="@+id/programme_end"
37
+            android:layout_width="wrap_content"
38
+            android:layout_height="wrap_content"
39
+            app:layout_constraintTop_toBottomOf="@id/programme_start"
40
+            app:layout_constraintBottom_toBottomOf="parent"
41
+            app:layout_constraintStart_toStartOf="@id/programme_start"
42
+            android:gravity="start"
43
+            android:textColor="@color/whited"
44
+            app:layout_constraintVertical_chainStyle="packed"
45
+
46
+            />
47
+    </androidx.constraintlayout.widget.ConstraintLayout>
48
+
49
+
50
+
51
+    <TextView
52
+        android:id="@+id/programme_name"
53
+        android:layout_width="0dp"
54
+        android:layout_height="wrap_content"
55
+        android:minHeight="32sp"
56
+        app:layout_constraintTop_toTopOf="@id/hours"
57
+        app:layout_constraintBottom_toBottomOf="@id/hours"
58
+        app:layout_constraintStart_toEndOf="@id/hours"
59
+        app:layout_constraintEnd_toEndOf="parent"
60
+        android:gravity="center_vertical"
61
+        android:textSize="16sp"
62
+        android:layout_marginStart="32dp"
63
+        android:layout_marginLeft="32dp"
64
+        android:textColor="@color/whited"
65
+        android:layout_marginTop="8dp"
66
+        android:layout_marginBottom="8dp"
67
+        />
68
+
69
+
70
+
71
+    <View
72
+        android:id="@+id/divider"
73
+        android:layout_width="match_parent"
74
+        android:layout_height="1dp"
75
+        android:background="?android:attr/listDivider"
76
+        app:layout_constraintStart_toStartOf="parent"
77
+        app:layout_constraintBottom_toBottomOf="parent"
78
+        app:layout_constraintEnd_toEndOf="parent"/>
79
+
80
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 1
app/src/main/res/layout/song_view.xml View File

4
     android:orientation="vertical"
4
     android:orientation="vertical"
5
     android:layout_width="match_parent"
5
     android:layout_width="match_parent"
6
     android:layout_height="wrap_content"
6
     android:layout_height="wrap_content"
7
+    android:layout_marginTop="8dp"
7
     android:layout_marginStart="8dp"
8
     android:layout_marginStart="8dp"
8
     android:layout_marginEnd="8dp"
9
     android:layout_marginEnd="8dp"
9
     >
10
     >
17
         app:layout_constraintBottom_toTopOf="@id/divider"
18
         app:layout_constraintBottom_toTopOf="@id/divider"
18
         app:layout_constraintStart_toStartOf="parent"
19
         app:layout_constraintStart_toStartOf="parent"
19
         app:layout_constraintEnd_toEndOf="parent"
20
         app:layout_constraintEnd_toEndOf="parent"
20
-        android:layout_marginTop="8dp"
21
         />
21
         />
22
 
22
 
23
     <View
23
     <View

+ 5 - 0
app/src/main/res/menu/bottom_nav_menu.xml View File

16
         android:icon="@drawable/ic_newspaper"
16
         android:icon="@drawable/ic_newspaper"
17
         android:title="@string/title_news"/>
17
         android:title="@string/title_news"/>
18
 
18
 
19
+    <item
20
+        android:id="@+id/navigation_programme"
21
+        android:icon="@drawable/ic_programme"
22
+        android:title="@string/programme"/>
23
+
19
 
24
 
20
 </menu>
25
 </menu>

+ 14 - 0
app/src/main/res/navigation/navigation_programme.xml View File

1
+<?xml version="1.0" encoding="utf-8"?>
2
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
3
+    xmlns:app="http://schemas.android.com/apk/res-auto"
4
+    xmlns:tools="http://schemas.android.com/tools"
5
+    android:id="@+id/navigation_programme"
6
+    app:startDestination="@id/fragment_programme"
7
+    tools:ignore="UnusedNavigation">
8
+
9
+    <fragment
10
+        android:id="@+id/fragment_programme"
11
+        android:name="fr.forum_thalie.tsumugi.ui.programme.ProgrammeFragment"
12
+        android:label="@string/title_programme" />
13
+
14
+</navigation>

+ 0 - 9
app/src/main/res/values/arrays.xml View File

1
 <?xml version="1.0" encoding="utf-8"?>
1
 <?xml version="1.0" encoding="utf-8"?>
2
 <resources>
2
 <resources>
3
 
3
 
4
-    <string-array name="weekdays">
5
-        <item name="monday">Monday</item>
6
-        <item name="tuesday">Tuesday</item>
7
-        <item name="wednesday">Wednesday</item>
8
-        <item name="thursday">Thursday</item>
9
-        <item name="friday">Friday</item>
10
-        <item name="saturday">Saturday</item>
11
-        <item name="sunday">Sunday</item>
12
-    </string-array>
13
     <string-array name="snoozeValues">
4
     <string-array name="snoozeValues">
14
         <item name="0">@string/disable</item>
5
         <item name="0">@string/disable</item>
15
         <item name="1">1</item>
6
         <item name="1">1</item>

+ 5 - 5
app/src/main/res/values/colors.xml View File

11
     <color name="whited3">#999999</color>
11
     <color name="whited3">#999999</color>
12
     <color name="whited4">#999999</color>
12
     <color name="whited4">#999999</color>
13
     <color name="whited5">#7F7F7F</color>
13
     <color name="whited5">#7F7F7F</color>
14
-    <color name="rblue">#3A7AAF</color>
14
+    <color name="rblue">#4FC3F7</color>
15
     <color name="bluereq">#527a8e</color>
15
     <color name="bluereq">#527a8e</color>
16
     <color name="blueTitle">#527a8e</color>
16
     <color name="blueTitle">#527a8e</color>
17
     <color name="reqButtonPressed">#5cb85c</color>
17
     <color name="reqButtonPressed">#5cb85c</color>
19
     <color name="reqButtonDisabled">#a15755</color>
19
     <color name="reqButtonDisabled">#a15755</color>
20
 
20
 
21
     <color name="seek_bar_background">#585858</color>
21
     <color name="seek_bar_background">#585858</color>
22
-    <color name="seek_bar_progress">#DF4C3A</color>
23
-    <color name="seek_bar_secondary_progress">#3A7AAF</color>
22
+    <color name="seek_bar_progress">#f58b01</color>
23
+    <color name="seek_bar_secondary_progress">#4FC3F7</color>
24
 
24
 
25
     <color name="progress_bar_background">#585858</color>
25
     <color name="progress_bar_background">#585858</color>
26
-    <color name="progress_bar_progress">#3A7AAF</color>
27
-    <color name="progress_bar_secondary_progress">#DF4C3A</color>
26
+    <color name="progress_bar_progress">#4FC3F7</color>
27
+    <color name="progress_bar_secondary_progress">#f58b01</color>
28
 
28
 
29
     <color name="white">#FFFFFF</color>
29
     <color name="white">#FFFFFF</color>
30
     <color name="black">#000000</color>
30
     <color name="black">#000000</color>

+ 9 - 2
app/src/main/res/values/strings.xml View File

4
     <string name="title_queue_lp">Songs</string>
4
     <string name="title_queue_lp">Songs</string>
5
     <string name="title_chat">Chat</string>
5
     <string name="title_chat">Chat</string>
6
     <string name="title_news">News</string>
6
     <string name="title_news">News</string>
7
+    <string name="title_programme">Planning</string>
7
 
8
 
8
     <string name="now_playing_notification_title_template">Now playing: %1$s</string>
9
     <string name="now_playing_notification_title_template">Now playing: %1$s</string>
9
 
10
 
13
     <string name="STREAM_URL_RSTLSS">http://stream.radiosolution.fr/rstlss.mp3</string>
14
     <string name="STREAM_URL_RSTLSS">http://stream.radiosolution.fr/rstlss.mp3</string>
14
 
15
 
15
     <string name="volume">Volume: </string>
16
     <string name="volume">Volume: </string>
16
-    <string name="MAIN_API">https://r-a-d.io/api</string>
17
 
17
 
18
-    <string name="up_next">Up next:</string>
18
+    <string name="up_next">Émission en cours :</string>
19
     <string name="now_streaming">Now streaming</string>
19
     <string name="now_streaming">Now streaming</string>
20
     <string name="error_webView">Error loading WebView. Try downloading Google Chrome on Google Play, or enabling it if you disabled it.</string>
20
     <string name="error_webView">Error loading WebView. Try downloading Google Chrome on Google Play, or enabling it if you disabled it.</string>
21
     <string name="action_settings">Settings</string>
21
     <string name="action_settings">Settings</string>
43
     <string name="disable">Disable</string>
43
     <string name="disable">Disable</string>
44
     <string name="website_url">https://tsumugi.forum-thalie.fr/</string>
44
     <string name="website_url">https://tsumugi.forum-thalie.fr/</string>
45
     <string name="rss_url">https://tsumugi.forum-thalie.fr/?feed=rss2</string>
45
     <string name="rss_url">https://tsumugi.forum-thalie.fr/?feed=rss2</string>
46
+    <string name="planning_url">ADD SOME URL HERE</string>
47
+    <string name="select_one_day">Select at least one day</string>
48
+    <string name="no_alarm_set">No alarm set</string>
49
+    <string name="next_alarm">Next alarm:</string>
50
+    <string name="lp">Last played</string>
51
+    <string name="programme">Planning</string>
52
+    <string name="regular_programme">Programme régulier</string>
46
 
53
 
47
 </resources>
54
 </resources>

+ 1 - 3
app/src/main/res/xml/alarm_preferences.xml View File

23
         app:iconSpaceReserved="false"
23
         app:iconSpaceReserved="false"
24
         app:title="Select days of the week"
24
         app:title="Select days of the week"
25
         app:singleLineTitle="false"
25
         app:singleLineTitle="false"
26
-        android:entries="@array/weekdays"
27
-        android:entryValues="@array/weekdays"
28
-        android:defaultValue="@array/weekdays"
26
+
29
         />
27
         />
30
 
28
 
31
     <ListPreference
29
     <ListPreference