소스 검색

removed many R/a/dio specifc code. Hid some UI that could be used later.

yattoz 4 년 전
부모
커밋
6b1c018318
81개의 변경된 파일163개의 추가작업 그리고 1800개의 파일을 삭제
  1. 0 23
      app/src/main/assets/chat.html
  2. BIN
      app/src/main/ic_launcher-web.png
  3. 1 1
      app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt
  4. 0 6
      app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt
  5. 4 17
      app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt
  6. 0 1
      app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt
  7. 5 43
      app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt
  8. 0 8
      app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt
  9. 13 8
      app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt
  10. 0 36
      app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt
  11. 0 63
      app/src/main/java/fr/forum_thalie/tsumugi/preferences/StreamerNotifServiceFragment.kt
  12. 0 41
      app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/ServiceNotification.kt
  13. 0 114
      app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorExtensions.kt
  14. 0 123
      app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorService.kt
  15. 0 35
      app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerNotification.kt
  16. 0 46
      app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/WorkerStore.kt
  17. 0 52
      app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatFragment.kt
  18. 0 12
      app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatViewModel.kt
  19. 0 47
      app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/WebViewChat.kt
  20. 2 52
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt
  21. 0 70
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/QueueFragment.kt
  22. 0 69
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/CooldownCalculator.kt
  23. 0 132
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/FavoritesFragment.kt
  24. 0 81
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestFragment.kt
  25. 0 42
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestResponse.kt
  26. 0 148
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestSongAdapter.kt
  27. 0 301
      app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/Requestor.kt
  28. 0 13
      app/src/main/res/drawable-anydpi-v24/lollipop_logo.xml
  29. 0 11
      app/src/main/res/drawable-anydpi/ic_chat_processing.xml
  30. 0 11
      app/src/main/res/drawable-anydpi/ic_open_in_browser.xml
  31. BIN
      app/src/main/res/drawable-hdpi/ic_chat_processing.png
  32. BIN
      app/src/main/res/drawable-hdpi/ic_open_in_browser.png
  33. BIN
      app/src/main/res/drawable-hdpi/lollipop_logo.png
  34. BIN
      app/src/main/res/drawable-mdpi/ic_chat_processing.png
  35. BIN
      app/src/main/res/drawable-mdpi/ic_open_in_browser.png
  36. BIN
      app/src/main/res/drawable-mdpi/lollipop_logo.png
  37. 2 2
      app/src/main/res/drawable-v23/launch_screen.xml
  38. BIN
      app/src/main/res/drawable-xhdpi/ic_chat_processing.png
  39. BIN
      app/src/main/res/drawable-xhdpi/ic_open_in_browser.png
  40. BIN
      app/src/main/res/drawable-xhdpi/lollipop_logo.png
  41. BIN
      app/src/main/res/drawable-xxhdpi/ic_chat_processing.png
  42. BIN
      app/src/main/res/drawable-xxhdpi/ic_open_in_browser.png
  43. BIN
      app/src/main/res/drawable-xxhdpi/lollipop_logo.png
  44. BIN
      app/src/main/res/drawable-xxxhdpi/lollipop_logo.png
  45. BIN
      app/src/main/res/drawable/actionbar_logo.jpg
  46. 42 0
      app/src/main/res/drawable/ic_launcher_foreground.xml
  47. 0 18
      app/src/main/res/drawable/ic_radio_logo_round.xml
  48. 0 18
      app/src/main/res/drawable/ic_radiologo.xml
  49. 59 0
      app/src/main/res/drawable/ic_tsumugi_lineart.xml
  50. BIN
      app/src/main/res/drawable/logo_roundsquare.png
  51. BIN
      app/src/main/res/drawable/normal_logo.png
  52. 0 42
      app/src/main/res/drawable/seek_bar_progress.xml
  53. 0 25
      app/src/main/res/drawable/seek_bar_thumb.xml
  54. 5 3
      app/src/main/res/layout/activity_main.xml
  55. 16 6
      app/src/main/res/layout/fragment_nowplaying.xml
  56. 0 4
      app/src/main/res/menu/bottom_nav_menu.xml
  57. 2 1
      app/src/main/res/menu/toolbar_menu.xml
  58. 2 2
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  59. 2 2
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  60. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  61. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  62. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  63. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  64. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  65. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  66. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  67. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  68. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  69. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  70. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  71. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  72. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  73. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  74. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  75. 0 13
      app/src/main/res/navigation/navigation_chat.xml
  76. 4 0
      app/src/main/res/values/ic_launcher_background.xml
  77. 1 1
      app/src/main/res/values/strings.xml
  78. 2 2
      app/src/main/res/values/styles.xml
  79. 1 26
      app/src/main/res/xml/customize_preferences.xml
  80. 0 7
      app/src/main/res/xml/preferences.xml
  81. 0 22
      app/src/main/res/xml/streamer_notif_service_preferences.xml

+ 0 - 23
app/src/main/assets/chat.html 파일 보기

@@ -1,23 +0,0 @@
1
-<!DOCTYPE html>
2
-<html lang="fr">
3
-
4
-<head>
5
-  <meta charset="utf-8">
6
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
-  <meta name="viewport" content="width=device-width, initial-scale=1.0,  maximum-scale=1.0, user-scalable=no">
8
-  <!-- seems like the font cannot be changed in KiwiIRC without writing a full theme, so loading the CSS below is useless-->
9
-  <!--
10
-  <link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" /> -->
11
-
12
-</head>
13
-
14
-<body>
15
-  <div style="height: auto">
16
-    <iframe src="https://kiwiirc.com/nextclient/?theme=dark#irc://irc.rizon.net:+6697/#r/a/dio"
17
-      style="border:0; width:100%; height:100%; position: absolute; top: 0px; left: 0px">
18
-    </iframe>
19
-  </div>
20
-
21
-</body>
22
-
23
-</html>

BIN
app/src/main/ic_launcher-web.png 파일 보기


+ 1 - 1
app/src/main/java/fr/forum_thalie/tsumugi/BaseNotification.kt 파일 보기

@@ -71,7 +71,7 @@ abstract class BaseNotification(private val notificationChannelId: String,
71 71
             builder.setSmallIcon(R.drawable.lollipop_logo)
72 72
             builder.color = -0xf58b01 // same color as Accent. Can't use c.getColor since it's API23+
73 73
         } else {
74
-            builder.setSmallIcon(R.drawable.normal_logo)
74
+            builder.setSmallIcon(R.drawable.logo_roundsquare)
75 75
         }
76 76
 
77 77
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

+ 0 - 6
app/src/main/java/fr/forum_thalie/tsumugi/BootBroadcastReceiver.kt 파일 보기

@@ -8,8 +8,6 @@ import android.util.Log
8 8
 import androidx.preference.PreferenceManager
9 9
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
10 10
 import fr.forum_thalie.tsumugi.playerstore.PlayerStore
11
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
12
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
13 11
 
14 12
 class BootBroadcastReceiver : BroadcastReceiver(){
15 13
 
@@ -19,8 +17,6 @@ class BootBroadcastReceiver : BroadcastReceiver(){
19 17
         preferenceStore = PreferenceManager.getDefaultSharedPreferences(context)
20 18
 
21 19
         if (arg1.action == Intent.ACTION_BOOT_COMPLETED) {
22
-            WorkerStore.instance.init(context)
23
-            startStreamerMonitor(context) // will actually start it only if enabled in settings
24 20
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
25 21
         }
26 22
 
@@ -29,8 +25,6 @@ class BootBroadcastReceiver : BroadcastReceiver(){
29 25
             RadioAlarm.instance.setNextAlarm(context) // schedule next alarm
30 26
             if (PlayerStore.instance.streamerName.value.isNullOrBlank())
31 27
                 PlayerStore.instance.initPicture(context)
32
-            if (!PlayerStore.instance.isInitialized)
33
-                PlayerStore.instance.initApi()
34 28
 
35 29
             val i = Intent(context, RadioService::class.java)
36 30
             i.putExtra("action", Actions.PLAY_OR_FALLBACK.name)

+ 4 - 17
app/src/main/java/fr/forum_thalie/tsumugi/MainActivity.kt 파일 보기

@@ -15,12 +15,8 @@ import fr.forum_thalie.tsumugi.playerstore.PlayerStore
15 15
 
16 16
 import java.util.Timer
17 17
 import android.view.MenuItem
18
-import androidx.preference.PreferenceManager
19 18
 import com.google.android.material.snackbar.Snackbar
20 19
 import fr.forum_thalie.tsumugi.alarm.RadioAlarm
21
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
22
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
23
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
24 20
 
25 21
 
26 22
 /* Log to file import
@@ -39,11 +35,11 @@ class MainActivity : BaseActivity() {
39 35
      * Called on first creation and when restoring state.
40 36
      */
41 37
     private fun setupBottomNavigationBar() {
42
-        val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
38
+        val bottomNavigationView : BottomNavigationView = findViewById(R.id.bottom_nav)
43 39
 
44 40
         //val navGraphIds = listOf(R.navigation.home, R.navigation.list, R.navigation.form)
45 41
         val navGraphIds = listOf(R.navigation.navigation_nowplaying, R.navigation.navigation_songs,
46
-            R.navigation.navigation_news, R.navigation.navigation_chat)
42
+            R.navigation.navigation_news)
47 43
 
48 44
         // Setup the bottom navigation view with a list of navigation graphs
49 45
         val controller = bottomNavigationView.setupWithNavController(
@@ -77,15 +73,15 @@ class MainActivity : BaseActivity() {
77 73
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
78 74
         // Handle item selection
79 75
         return when (item.itemId) {
76
+            /*
80 77
             R.id.action_refresh -> {
81 78
                 PlayerStore.instance.queue.clear()
82 79
                 PlayerStore.instance.lp.clear()
83
-                PlayerStore.instance.initApi()
84
-                Requestor.instance.initFavorites()
85 80
                 val s = Snackbar.make(findViewById(R.id.nav_host_container), "Refreshing data..." as CharSequence, Snackbar.LENGTH_LONG)
86 81
                 s.show()
87 82
                 true
88 83
             }
84
+            */
89 85
             R.id.action_settings -> {
90 86
                 val i = Intent(this, ParametersActivity::class.java)
91 87
                 startActivity(i)
@@ -118,10 +114,6 @@ class MainActivity : BaseActivity() {
118 114
 
119 115
     override fun onCreate(savedInstanceState: Bundle?) {
120 116
         super.onCreate(savedInstanceState)
121
-
122
-        WorkerStore.instance.init(this)
123
-        startStreamerMonitor(this) // this checks the preferenceStore before actually starting a service, don't worry.
124
-
125 117
         RadioAlarm.instance.cancelAlarm(c = this)
126 118
         RadioAlarm.instance.setNextAlarm(c = this) // this checks the preferenceStore before actually setting an alarm, don't worry.
127 119
 
@@ -132,8 +124,6 @@ class MainActivity : BaseActivity() {
132 124
         colorRedList = (ResourcesCompat.getColorStateList(resources, R.color.button_red, null))
133 125
         colorGreenListCompat = (ResourcesCompat.getColorStateList(resources, R.color.button_green_compat, null))
134 126
 
135
-        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.
136
-
137 127
         // Post-UI Launch
138 128
         if (PlayerStore.instance.isInitialized)
139 129
         {
@@ -149,9 +139,6 @@ class MainActivity : BaseActivity() {
149 139
             // initialize some API data
150 140
             PlayerStore.instance.initPicture(this)
151 141
             PlayerStore.instance.streamerName.value = "" // initializing the streamer name will trigger an initApi from the observer in the Service.
152
-
153
-            // initialize the favorites
154
-            Requestor.instance.initFavorites()
155 142
         }
156 143
 
157 144
         if (!isTimerStarted)

+ 0 - 1
app/src/main/java/fr/forum_thalie/tsumugi/ParametersActivity.kt 파일 보기

@@ -29,7 +29,6 @@ class ParametersActivity : BaseActivity() {
29 29
             ActionOpenParam.ALARM.name -> AlarmFragment()
30 30
             ActionOpenParam.SLEEP.name -> SleepFragment()
31 31
             ActionOpenParam.CUSTOMIZE.name -> CustomizeFragment()
32
-            ActionOpenParam.STREAMER_NOTIFICATION_SERVICE.name -> StreamerNotifServiceFragment()
33 32
             else -> MainPreferenceFragment()
34 33
         }
35 34
 

+ 5 - 43
app/src/main/java/fr/forum_thalie/tsumugi/RadioService.kt 파일 보기

@@ -107,29 +107,6 @@ class RadioService : MediaBrowserServiceCompat() {
107 107
     // ################### OBSERVERS ####################
108 108
     // ##################################################
109 109
 
110
-    private val titleObserver: Observer<String> = Observer {
111
-        if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_PLAYING)
112
-        {
113
-            Log.d(tag, radioTag + "SONG CHANGED AND PLAYING")
114
-            // we activate latency compensation only if it's been at least 2 songs...
115
-            when {
116
-                PlayerStore.instance.isStreamDown -> {
117
-                    // if we reach here, it means that the observer has been called by a new song and that the stream was down previously.
118
-                    // so the stream is now back to normal.
119
-                    PlayerStore.instance.isStreamDown = false
120
-                    PlayerStore.instance.initApi()
121
-                }
122
-                PlayerStore.instance.currentSong.title.value == getString(R.string.ed) -> {
123
-                    PlayerStore.instance.isStreamDown = true
124
-                }
125
-                else -> {
126
-                    PlayerStore.instance.fetchApi(numberOfSongs >= 2)
127
-                }
128
-            }
129
-        }
130
-        nowPlayingNotification.update(this)
131
-    }
132
-
133 110
     private val volumeObserver: Observer<Int> = Observer {
134 111
         setVolume(it)
135 112
     }
@@ -145,20 +122,14 @@ class RadioService : MediaBrowserServiceCompat() {
145 122
             stopPlaying()
146 123
     }
147 124
 
148
-    private val startTimeObserver = Observer<Long> {
149
-        // We're listening to startTime to determine if we have to update Queue and Lp.
150
-        // this is because startTime is set by the API and never by the ICY, so both cases are covered (playing and stopped)
151
-        // should be OK even when a new streamer comes in.
152
-        if (it != PlayerStore.instance.currentSongBackup.startTime.value) // we have a new song
125
+    private val titleObserver = Observer<String> {
126
+        // We're checking if a new song arrives. If so, we put the currentSong in Lp and update the backup.
127
+        if (PlayerStore.instance.currentSong != PlayerStore.instance.currentSongBackup
128
+            && it != noConnectionValue)
153 129
         {
154 130
             PlayerStore.instance.updateLp()
155
-            PlayerStore.instance.updateQueue()
156 131
         }
157
-    }
158
-
159
-    private val streamerObserver = Observer<String> {
160
-        PlayerStore.instance.initApi()
161
-        nowPlayingNotification.update(this) // should update the streamer icon
132
+        nowPlayingNotification.update(this)
162 133
     }
163 134
 
164 135
     private val streamerPictureObserver = Observer<Bitmap> {
@@ -224,9 +195,7 @@ class RadioService : MediaBrowserServiceCompat() {
224 195
         nowPlayingNotification.create(this, mediaSession)
225 196
 
226 197
 
227
-        PlayerStore.instance.streamerName.observeForever(streamerObserver)
228 198
         PlayerStore.instance.currentSong.title.observeForever(titleObserver)
229
-        PlayerStore.instance.currentSong.startTime.observeForever(startTimeObserver)
230 199
         PlayerStore.instance.volume.observeForever(volumeObserver)
231 200
         PlayerStore.instance.isPlaying.observeForever(isPlayingObserver)
232 201
         PlayerStore.instance.isMuted.observeForever(isMutedObserver)
@@ -234,12 +203,6 @@ class RadioService : MediaBrowserServiceCompat() {
234 203
 
235 204
         startForeground(radioServiceId, nowPlayingNotification.notification)
236 205
 
237
-        // start ticker for when the player is stopped
238
-        val periodString = PreferenceManager.getDefaultSharedPreferences(this).getString("fetchPeriod", "10") ?: "10"
239
-        val period: Long = Integer.parseInt(periodString).toLong()
240
-        if (period > 0)
241
-            apiTicker.schedule(ApiFetchTick(), 0, period * 1000)
242
-
243 206
         PlayerStore.instance.isServiceStarted.value = true
244 207
         Log.d(tag, radioTag + "created")
245 208
     }
@@ -303,7 +266,6 @@ class RadioService : MediaBrowserServiceCompat() {
303 266
         player.release()
304 267
         unregisterReceiver(receiver)
305 268
         PlayerStore.instance.currentSong.title.removeObserver(titleObserver)
306
-        PlayerStore.instance.currentSong.startTime.removeObserver(startTimeObserver)
307 269
         PlayerStore.instance.volume.removeObserver(volumeObserver)
308 270
         PlayerStore.instance.isPlaying.removeObserver(isPlayingObserver)
309 271
         PlayerStore.instance.isMuted.removeObserver(isMutedObserver)

+ 0 - 8
app/src/main/java/fr/forum_thalie/tsumugi/Tickers.kt 파일 보기

@@ -11,11 +11,3 @@ class Tick  : TimerTask() {
11 11
     }
12 12
 }
13 13
 
14
-class ApiFetchTick  : TimerTask() {
15
-    override fun run() {
16
-        if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_STOPPED)
17
-        {
18
-            PlayerStore.instance.fetchApi()
19
-        }
20
-    }
21
-}

+ 13 - 8
app/src/main/java/fr/forum_thalie/tsumugi/playerstore/PlayerStore.kt 파일 보기

@@ -31,10 +31,8 @@ class PlayerStore {
31 31
     val isLpUpdated: MutableLiveData<Boolean> = MutableLiveData()
32 32
     val isMuted : MutableLiveData<Boolean> = MutableLiveData()
33 33
     val listenersCount: MutableLiveData<Int> = MutableLiveData()
34
-    private val urlToScrape = "https://r-a-d.io/api"
35 34
     var latencyCompensator : Long = 0
36 35
     var isInitialized: Boolean = false
37
-    var isStreamDown: Boolean = false
38 36
 
39 37
     init {
40 38
         playbackState.value = PlaybackStateCompat.STATE_STOPPED
@@ -47,6 +45,7 @@ class PlayerStore {
47 45
         isLpUpdated.value = false
48 46
         isMuted.value = false
49 47
         currentSong.title.value = noConnectionValue
48
+        currentSongBackup.title.value = noConnectionValue
50 49
         listenersCount.value = 0
51 50
     }
52 51
 
@@ -54,6 +53,7 @@ class PlayerStore {
54 53
     // ################# API FUNCTIONS ##################
55 54
     // ##################################################
56 55
 
56
+    /*
57 57
     private fun updateApi(resMain: JSONObject, isCompensatingLatency : Boolean = false) {
58 58
         // If we're not in PLAYING state, update title / artist metadata. If we're playing, the ICY will take care of that.
59 59
         if (playbackState.value != PlaybackStateCompat.STATE_PLAYING || currentSong.title.value.isNullOrEmpty()
@@ -154,24 +154,26 @@ class PlayerStore {
154 154
         }
155 155
         Async(scrape, post)
156 156
     }
157
+    */
157 158
 
158 159
     // ##################################################
159 160
     // ############## QUEUE / LP FUNCTIONS ##############
160 161
     // ##################################################
161 162
 
162 163
     fun updateLp() {
163
-        // note : lp must never be empty. There should always be some songs "last played".
164
-        // if not, then the function has been called before initialization. No need to do anything.
165
-        if (lp.isNotEmpty()){
164
+        // note : lp is empty at initialization. This check was needed when we used the R/a/dio API.
165
+        //if (lp.isNotEmpty()){
166 166
             val n = Song()
167 167
             n.copy(currentSongBackup)
168
-            lp.add(0, n)
168
+            if (n.title.value != noConnectionValue)
169
+                lp.add(0, n)
169 170
             currentSongBackup.copy(currentSong)
170 171
             isLpUpdated.value = true
171 172
             Log.d(tag, playerStoreTag +  lp.toString())
172
-        }
173
+        //}
173 174
     }
174 175
 
176
+    /*
175 177
     fun updateQueue() {
176 178
         if (queue.isNotEmpty()) {
177 179
             queue.remove(queue.first())
@@ -257,11 +259,13 @@ class PlayerStore {
257 259
         song.type.value = songJSON.getInt("type")
258 260
         return song
259 261
     }
262
+    */
260 263
 
261 264
     // ##################################################
262 265
     // ############## PICTURE FUNCTIONS #################
263 266
     // ##################################################
264 267
 
268
+    /*
265 269
     private fun fetchPicture(fileUrl: String)
266 270
     {
267 271
         val scrape: (Any?) -> Bitmap? = {
@@ -287,10 +291,11 @@ class PlayerStore {
287 291
         }
288 292
         Async(scrape, post)
289 293
     }
294
+     */
290 295
 
291 296
     fun initPicture(c: Context) {
292 297
         streamerPicture.value = BitmapFactory.decodeResource(c.resources,
293
-            R.drawable.actionbar_logo
298
+            R.drawable.logo_roundsquare
294 299
         )
295 300
     }
296 301
 

+ 0 - 36
app/src/main/java/fr/forum_thalie/tsumugi/preferences/CustomizeFragment.kt 파일 보기

@@ -8,19 +8,11 @@ import androidx.appcompat.app.AlertDialog
8 8
 import androidx.preference.*
9 9
 import fr.forum_thalie.tsumugi.R
10 10
 import fr.forum_thalie.tsumugi.preferenceStore
11
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
12 11
 
13 12
 class CustomizeFragment : PreferenceFragmentCompat() {
14 13
     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
15 14
         setPreferencesFromResource(R.xml.customize_preferences, rootKey)
16 15
 
17
-        val userNamePref = preferenceScreen.findPreference<EditTextPreference>("userName")
18
-        userNamePref?.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
19
-        userNamePref?.setOnPreferenceChangeListener { _, newValue ->
20
-            val name = newValue as String
21
-            Requestor.instance.initFavorites(name) // need to be as parameter cause the callback is called BEFORE PARAMETER SET
22
-            true
23
-        }
24 16
 
25 17
         val snackbarPersistent = preferenceScreen.findPreference<SwitchPreferenceCompat>("snackbarPersistent")
26 18
         snackbarPersistent!!.summary = if (preferenceStore.getBoolean("snackbarPersistent", true))
@@ -48,33 +40,5 @@ class CustomizeFragment : PreferenceFragmentCompat() {
48 40
             true
49 41
         }
50 42
 
51
-        val helpFavorites = preferenceScreen.findPreference<Preference>("helpFavorites")
52
-        helpFavorites?.setOnPreferenceClickListener { _ ->
53
-            val url = getString(R.string.github_url_wiki_irc_for_favorites)
54
-            val i = Intent(Intent.ACTION_VIEW)
55
-            i.data = Uri.parse(url)
56
-            startActivity(i)
57
-            true
58
-        }
59
-
60
-        val fetchPeriod = preferenceScreen.findPreference<ListPreference>("fetchPeriod")
61
-        fetchPeriod?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
62
-        fetchPeriod?.setOnPreferenceChangeListener { _, newValue ->
63
-            val builder1 = AlertDialog.Builder(context!!)
64
-            if (Integer.parseInt(newValue as String) == 0)
65
-                builder1.setMessage(R.string.fetch_disabled_restart_the_app)
66
-            else
67
-                builder1.setMessage(R.string.restart_the_app)
68
-            builder1.setCancelable(true)
69
-
70
-            builder1.setPositiveButton("Close" ) { dialog, _ ->
71
-                dialog.cancel()
72
-            }
73
-
74
-            val alert11 = builder1.create()
75
-            alert11.show()
76
-            true
77
-        }
78
-
79 43
     }
80 44
 }

+ 0 - 63
app/src/main/java/fr/forum_thalie/tsumugi/preferences/StreamerNotifServiceFragment.kt 파일 보기

@@ -1,63 +0,0 @@
1
-package fr.forum_thalie.tsumugi.preferences
2
-
3
-import android.os.Bundle
4
-import androidx.appcompat.app.AlertDialog
5
-import androidx.preference.*
6
-import fr.forum_thalie.tsumugi.R
7
-import fr.forum_thalie.tsumugi.preferenceStore
8
-import fr.forum_thalie.tsumugi.streamerNotificationService.WorkerStore
9
-import fr.forum_thalie.tsumugi.streamerNotificationService.startStreamerMonitor
10
-import fr.forum_thalie.tsumugi.streamerNotificationService.stopStreamerMonitor
11
-
12
-class StreamerNotifServiceFragment : PreferenceFragmentCompat() {
13
-    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
14
-        setPreferencesFromResource(R.xml.streamer_notif_service_preferences, rootKey)
15
-
16
-
17
-        val streamerPeriod = preferenceScreen.findPreference<Preference>("streamerMonitorPeriodPref")
18
-
19
-        val streamerNotification = preferenceScreen.findPreference<Preference>("newStreamerNotification")
20
-        streamerNotification?.setOnPreferenceChangeListener { _, newValue ->
21
-            if ((newValue as Boolean)) {
22
-                val builder1 = AlertDialog.Builder(context!!)
23
-                builder1.setMessage(R.string.warningStreamerNotif)
24
-                builder1.setCancelable(false)
25
-                builder1.setPositiveButton(
26
-                    "Yes"
27
-                ) { dialog, _ ->
28
-                    startStreamerMonitor(context!!, force = true) // force enabled because the preference value is not yet set when running this callback.
29
-                    streamerPeriod?.isEnabled = true
30
-                    dialog.cancel()
31
-                }
32
-
33
-                builder1.setNegativeButton(
34
-                    "No"
35
-                ) { dialog, _ ->
36
-
37
-                    stopStreamerMonitor(context!!)
38
-                    (streamerNotification as SwitchPreferenceCompat).isChecked = false
39
-                    dialog.cancel()
40
-                }
41
-
42
-                val alert11 = builder1.create()
43
-                alert11.show()
44
-            }
45
-            else {
46
-                stopStreamerMonitor(context!!)
47
-                streamerPeriod?.isEnabled = false
48
-                WorkerStore.instance.isServiceStarted = false
49
-            }
50
-            true
51
-        }
52
-
53
-        streamerPeriod?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
54
-        streamerPeriod?.isEnabled = preferenceStore.getBoolean("newStreamerNotification", true)
55
-        streamerPeriod?.setOnPreferenceChangeListener { _, newValue ->
56
-            WorkerStore.instance.tickerPeriod = (Integer.parseInt(newValue as String)).toLong() * 60
57
-            // this should be sufficient, the next alarm schedule should take the new tickerPeriod.
58
-            true
59
-        }
60
-
61
-
62
-    }
63
-}

+ 0 - 41
app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/ServiceNotification.kt 파일 보기

@@ -1,41 +0,0 @@
1
-package fr.forum_thalie.tsumugi.streamerNotificationService
2
-
3
-import android.content.Context
4
-import fr.forum_thalie.tsumugi.BaseNotification
5
-import java.util.*
6
-
7
-class ServiceNotification(
8
-    notificationChannelId: String,
9
-    notificationChannel : Int,
10
-    notificationId: Int,
11
-    notificationImportance: Int
12
-
13
-) : BaseNotification
14
-    (
15
-    notificationChannelId,
16
-    notificationChannel,
17
-    notificationId,
18
-    notificationImportance
19
-){
20
-    
21
-    override fun create(c: Context)
22
-    {
23
-        super.create(c)
24
-        update()
25
-    }
26
-
27
-    fun update()
28
-    {
29
-        val date = Date()   // given date
30
-        val calendar = Calendar.getInstance() // creates a new calendar instance
31
-        calendar.time = date   // assigns calendar to given date
32
-        val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
33
-        //val hours_american = calendar.get(Calendar.HOUR)        // gets hour in 12h format
34
-        val minutes = calendar.get(Calendar.MINUTE)
35
-        val seconds = calendar.get(Calendar.SECOND)
36
-
37
-        builder.setContentTitle("Never miss a stream! Current: ${WorkerStore.instance.streamerName.value}")
38
-        builder.setContentText("Last update: ${hours}:${minutes}:${seconds}")
39
-        notification = builder.build()
40
-    }
41
-}

+ 0 - 114
app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorExtensions.kt 파일 보기

@@ -1,114 +0,0 @@
1
-package fr.forum_thalie.tsumugi.streamerNotificationService
2
-
3
-import android.app.AlarmManager
4
-import android.app.PendingIntent
5
-import android.content.BroadcastReceiver
6
-import android.content.Context
7
-import android.content.Intent
8
-import android.os.Build
9
-import android.os.SystemClock
10
-import android.util.Log
11
-import androidx.core.app.NotificationCompat
12
-import androidx.preference.PreferenceManager
13
-import fr.forum_thalie.tsumugi.*
14
-import fr.forum_thalie.tsumugi.alarm.RadioAlarm
15
-import fr.forum_thalie.tsumugi.playerstore.PlayerStore
16
-import org.json.JSONObject
17
-import java.net.URL
18
-
19
-fun startNextAlarmStreamer(c: Context){
20
-    // the notification works with an alarm re-scheduled at fixed rate.
21
-    // if the service stopped, the alarm is not re-scheduled.
22
-    if (WorkerStore.instance.isServiceStarted)
23
-    {
24
-        val alarmIntent = Intent(c, StreamerMonitorService::class.java).let { intent ->
25
-            intent.putExtra("action", Actions.NOTIFY.name)
26
-            PendingIntent.getService(c, 0, intent, 0)
27
-        }
28
-
29
-        val alarmMgr = c.getSystemService(Context.ALARM_SERVICE) as AlarmManager
30
-        when {
31
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> alarmMgr.setExactAndAllowWhileIdle(
32
-                AlarmManager.ELAPSED_REALTIME_WAKEUP,
33
-                SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
34
-                alarmIntent
35
-            )
36
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> alarmMgr.setExact(
37
-                AlarmManager.ELAPSED_REALTIME_WAKEUP,
38
-                SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
39
-                alarmIntent
40
-            )
41
-            else -> alarmMgr.set(
42
-                AlarmManager.ELAPSED_REALTIME_WAKEUP,
43
-                SystemClock.elapsedRealtime() + WorkerStore.instance.tickerPeriod * 1000,
44
-                alarmIntent
45
-            )
46
-        }
47
-    } else {
48
-        Log.d(tag, "alarm called while service is dead - skipped.")
49
-    }
50
-}
51
-
52
-fun stopStreamerMonitor(context: Context)
53
-{
54
-    val intent = Intent(context, StreamerMonitorService::class.java)
55
-    intent.putExtra("action", Actions.KILL.name)
56
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
57
-        context.startService(intent)
58
-    } else {
59
-        context.startService(intent)
60
-    }
61
-
62
-    Log.i(tag, "Service stopped")
63
-}
64
-
65
-fun startStreamerMonitor(context: Context, force: Boolean = false)
66
-{
67
-    if (!force)
68
-    {
69
-        val isNotifyingForNewStreamer = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("newStreamerNotification", false)
70
-        if (!isNotifyingForNewStreamer)
71
-                return
72
-    }
73
-
74
-    val intent = Intent(context, StreamerMonitorService::class.java)
75
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
76
-        context.startForegroundService(intent)
77
-    } else {
78
-        context.startService(intent)
79
-    }
80
-
81
-    Log.i(tag, "Service started on boot")
82
-}
83
-
84
-fun fetchStreamer(applicationContext: Context) {
85
-    val urlToScrape = "https://r-a-d.io/api"
86
-    val scrape : (Any?) -> String =
87
-        {
88
-            URL(urlToScrape).readText()
89
-        }
90
-    val post: (parameter: Any?) -> Unit = {
91
-        val result = JSONObject(it as String)
92
-        if (!result.isNull("main"))
93
-        {
94
-            val name = result.getJSONObject("main").getJSONObject("dj").getString("djname")
95
-            WorkerStore.instance.streamerName.value = name
96
-        }
97
-    }
98
-
99
-    // notify
100
-    val t = ServiceNotification(
101
-        notificationChannelId = applicationContext.getString(R.string.streamerServiceChannelId),
102
-        notificationChannel = R.string.streamerServiceChannel,
103
-        notificationId = 2,
104
-        notificationImportance = NotificationCompat.PRIORITY_LOW
105
-    )
106
-    t.create(applicationContext)
107
-    t.show()
108
-
109
-    try{
110
-        Async(scrape, post)
111
-        Log.d(tag, "enqueue next work in ${WorkerStore.instance.tickerPeriod} seconds")
112
-    } catch (e: Exception) {
113
-    }
114
-}

+ 0 - 123
app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerMonitorService.kt 파일 보기

@@ -1,123 +0,0 @@
1
-package fr.forum_thalie.tsumugi.streamerNotificationService
2
-
3
-
4
-import android.app.Service
5
-import android.content.Intent
6
-import android.os.Build
7
-import android.os.IBinder
8
-import android.util.Log
9
-import androidx.core.app.NotificationCompat
10
-import androidx.lifecycle.Observer
11
-import androidx.preference.PreferenceManager
12
-import fr.forum_thalie.tsumugi.Actions
13
-import fr.forum_thalie.tsumugi.R
14
-import fr.forum_thalie.tsumugi.tag
15
-import java.util.*
16
-
17
-class StreamerMonitorService : Service() {
18
-    override fun onBind(intent: Intent): IBinder? {
19
-        return null     // no binding allowed nor needed
20
-    }
21
-    private val streamerNameObserver: Observer<String> = Observer {
22
-        val previousStreamer: String
23
-        if (PreferenceManager.getDefaultSharedPreferences(this).contains("streamerName"))
24
-        {
25
-            previousStreamer = PreferenceManager.getDefaultSharedPreferences(this).getString("streamerName", "") ?: ""
26
-            /* 3 conditions:
27
-                - the streamer changed from previously
28
-                - there is a previous non-empty streamer (at least second time running it)
29
-                - the current streamer is non-empty (this can happen at Activity start where init() is called)
30
-             */
31
-            if (previousStreamer != it && previousStreamer != "" && it != "")
32
-            {
33
-                // notify
34
-                val newStreamer = StreamerNotification(
35
-                    notificationChannelId = this.getString(R.string.streamerNotificationChannelId),
36
-                    notificationChannel = R.string.streamerNotificationChannel,
37
-                    notificationId = 3,
38
-                    notificationImportance = NotificationCompat.PRIORITY_DEFAULT
39
-                )
40
-                newStreamer.create(this)
41
-                newStreamer.show()
42
-            }
43
-        }
44
-
45
-        with(PreferenceManager.getDefaultSharedPreferences(this).edit()){
46
-            putString("streamerName", it)
47
-            commit()
48
-        }
49
-    }
50
-
51
-    override fun onCreate() {
52
-        super.onCreate()
53
-        val streamerMonitorNotification = ServiceNotification(
54
-            notificationChannelId = this.getString(R.string.streamerServiceChannelId),
55
-            notificationChannel = R.string.streamerServiceChannel,
56
-            notificationId = 2,
57
-            notificationImportance = NotificationCompat.PRIORITY_LOW
58
-        )
59
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
60
-        {
61
-            streamerMonitorNotification.create(this)
62
-            streamerMonitorNotification.update()
63
-            streamerMonitorNotification.show()
64
-            startForeground(2, streamerMonitorNotification.notification)
65
-        }
66
-
67
-        WorkerStore.instance.tickerPeriod = 60 *
68
-                (if (PreferenceManager.getDefaultSharedPreferences(this).contains("streamerMonitorPeriodPref"))
69
-                    Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(this).getString("streamerMonitorPeriodPref", "15")!!).toLong()
70
-                else
71
-                    15)
72
-        Log.d(tag, "tickerPeriod = ${WorkerStore.instance.tickerPeriod}")
73
-
74
-        with(PreferenceManager.getDefaultSharedPreferences(this).edit()){
75
-            remove("streamerName")
76
-            commit() // I commit on main thread to be sure it's been updated before continuing.
77
-        }
78
-        WorkerStore.instance.streamerName.observeForever(streamerNameObserver)
79
-        WorkerStore.instance.isServiceStarted = true
80
-        startNextAlarmStreamer(this)
81
-        Log.d(tag, "streamerMonitor created")
82
-    }
83
-
84
-    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
85
-        val isNotifyingForNewStreamer = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("newStreamerNotification", false)
86
-
87
-        // it's probably redundant but it shouldn't hurt
88
-        if (!isNotifyingForNewStreamer || !WorkerStore.instance.isServiceStarted)
89
-        {
90
-            stopForeground(true)
91
-            stopSelf()
92
-            return START_NOT_STICKY
93
-        }
94
-        when (intent?.getStringExtra("action")) {
95
-            Actions.NOTIFY.name -> {
96
-                val date = Date()   // given date
97
-                val calendar = Calendar.getInstance() // creates a new calendar instance
98
-                calendar.time = date   // assigns calendar to given date
99
-                val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
100
-                //val hours_american = calendar.get(Calendar.HOUR)        // gets hour in 12h format
101
-                val minutes = calendar.get(Calendar.MINUTE)       // gets month number, NOTE this is zero based!
102
-
103
-                Log.d(tag, "Fetched streamer name at ${hours}:${if (minutes < 10) "0" else ""}${minutes}")
104
-                fetchStreamer(this)
105
-                startNextAlarmStreamer(this) // schedule next alarm
106
-                return START_STICKY
107
-            }
108
-            Actions.KILL.name -> {
109
-                stopForeground(true)
110
-                stopSelf()
111
-                return START_NOT_STICKY
112
-            }
113
-        }
114
-        return START_STICKY
115
-        //super.onStartCommand(intent, flags, startId)
116
-    }
117
-
118
-    override fun onDestroy() {
119
-        WorkerStore.instance.streamerName.removeObserver(streamerNameObserver)
120
-        WorkerStore.instance.isServiceStarted = false
121
-        super.onDestroy()
122
-    }
123
-}

+ 0 - 35
app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/StreamerNotification.kt 파일 보기

@@ -1,35 +0,0 @@
1
-package fr.forum_thalie.tsumugi.streamerNotificationService
2
-
3
-import android.content.Context
4
-import fr.forum_thalie.tsumugi.BaseNotification
5
-import java.util.*
6
-
7
-class StreamerNotification(
8
-    notificationChannelId: String,
9
-    notificationChannel : Int,
10
-    notificationId: Int,
11
-    notificationImportance: Int
12
-
13
-) : BaseNotification
14
-    (
15
-    notificationChannelId,
16
-    notificationChannel,
17
-    notificationId,
18
-    notificationImportance
19
-){
20
-    override fun create(c: Context) {
21
-        super.create(c)
22
-        val date = Date()   // given date
23
-        val calendar = Calendar.getInstance() // creates a new calendar instance
24
-        calendar.time = date   // assigns calendar to given date
25
-        val hours = calendar.get(Calendar.HOUR_OF_DAY) // gets hour in 24h format
26
-        //val hours_american = calendar.get(Calendar.HOUR)        // gets hour in 12h format
27
-        val minutes = calendar.get(Calendar.MINUTE)       // gets month number, NOTE this is zero based!
28
-
29
-        builder.setContentTitle("${WorkerStore.instance.streamerName.value} started streaming!")
30
-        builder.setContentText("Started at ${hours}:${if (minutes < 10) "0" else ""}${minutes}")
31
-        builder.setAutoCancel(true)
32
-        super.show()
33
-    }
34
-
35
-}

+ 0 - 46
app/src/main/java/fr/forum_thalie/tsumugi/streamerNotificationService/WorkerStore.kt 파일 보기

@@ -1,46 +0,0 @@
1
-package fr.forum_thalie.tsumugi.streamerNotificationService
2
-
3
-import android.content.Context
4
-import android.util.Log
5
-import androidx.core.app.NotificationCompat
6
-import androidx.lifecycle.MutableLiveData
7
-import androidx.lifecycle.Observer
8
-import androidx.preference.PreferenceManager
9
-import fr.forum_thalie.tsumugi.R
10
-import fr.forum_thalie.tsumugi.tag
11
-
12
-class WorkerStore {
13
-    companion object {
14
-        val instance = WorkerStore()
15
-    }
16
-
17
-    val streamerName = MutableLiveData<String>()
18
-    var isServiceStarted : Boolean = false
19
-    var tickerPeriod : Long = 45 // seconds
20
-
21
-    init {
22
-        tickerPeriod = 45
23
-        streamerName.value = ""
24
-        isServiceStarted = false
25
-    }
26
-
27
-    fun init(c: Context)
28
-    {
29
-        tickerPeriod = 45
30
-        streamerName.value = ""
31
-        val tickerPeriod = 60 *
32
-                (if (PreferenceManager.getDefaultSharedPreferences(c).contains("streamerMonitorPeriodPref"))
33
-                    Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(c).getString("streamerMonitorPeriodPref", "15")!!).toLong()
34
-                else
35
-                    15
36
-                        )
37
-        instance.tickerPeriod = tickerPeriod
38
-        Log.d(tag, "tickerPeriod = $tickerPeriod")
39
-
40
-        with(PreferenceManager.getDefaultSharedPreferences(c).edit()){
41
-            remove("streamerName")
42
-            commit() // I commit on main thread to be sure it's been updated before continuing.
43
-        }
44
-    }
45
-
46
-}

+ 0 - 52
app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatFragment.kt 파일 보기

@@ -1,52 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.chat
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 android.view.WindowManager
9
-import android.webkit.WebView
10
-import androidx.fragment.app.Fragment
11
-import androidx.lifecycle.ViewModelProviders
12
-import fr.forum_thalie.tsumugi.R
13
-
14
-
15
-class ChatFragment : Fragment() {
16
-
17
-    private lateinit var chatViewModel: ChatViewModel
18
-
19
-
20
-    override fun onCreateView(
21
-        inflater: LayoutInflater,
22
-        container: ViewGroup?,
23
-        savedInstanceState: Bundle?
24
-    ): View? {
25
-        activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
26
-
27
-        chatViewModel =
28
-                ViewModelProviders.of(this).get(ChatViewModel::class.java)
29
-
30
-        if (!chatViewModel.isChatLoaded)
31
-        {
32
-
33
-            try {
34
-                chatViewModel.root = inflater.inflate(R.layout.fragment_chat, container, false)
35
-                chatViewModel.webView = chatViewModel.root.findViewById<WebView>(R.id.chat_webview)
36
-                chatViewModel.webViewChat = WebViewChat(chatViewModel.webView as WebView)
37
-                chatViewModel.webViewChat!!.start()
38
-            } catch (e: Exception) {
39
-                chatViewModel.root = inflater.inflate(R.layout.fragment_error_chat, container, false)
40
-            }
41
-
42
-            chatViewModel.isChatLoaded = true
43
-            Log.d(tag, "webview created")
44
-        } else {
45
-            Log.d(tag, "webview already created!?")
46
-        }
47
-
48
-        return chatViewModel.root
49
-    }
50
-
51
-
52
-}

+ 0 - 12
app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/ChatViewModel.kt 파일 보기

@@ -1,12 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.chat
2
-
3
-import android.view.View
4
-import android.webkit.WebView
5
-import androidx.lifecycle.ViewModel
6
-
7
-class ChatViewModel : ViewModel() {
8
-    lateinit var root: View
9
-    var webView: WebView? = null
10
-    var isChatLoaded = false
11
-    var webViewChat: WebViewChat? = null
12
-}

+ 0 - 47
app/src/main/java/fr/forum_thalie/tsumugi/ui/chat/WebViewChat.kt 파일 보기

@@ -1,47 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.chat
2
-
3
-import android.annotation.SuppressLint
4
-import android.content.Intent
5
-import android.net.Uri
6
-import android.webkit.WebChromeClient
7
-import android.webkit.WebView
8
-
9
-class WebViewChat(private val webView: WebView) {
10
-
11
-        @SuppressLint("SetJavaScriptEnabled")
12
-        fun start() {
13
-
14
-            val webSetting = this.webView.settings
15
-            webSetting.javaScriptEnabled = true
16
-            webSetting.setSupportZoom(false)
17
-
18
-            /* TODO: in the future, it could be nice to have a parameters screen where you can:
19
-         - Set the text zoom
20
-         - Set your username (to not type it every time, would it be possible?)
21
-         - Hide the chat?
22
-         - do more? */
23
-            webSetting.textZoom = 90
24
-
25
-            webSetting.setSupportMultipleWindows(true)
26
-            // needs to open target="_blank" links as KiwiIRC links have this attribute.
27
-            // shamelessly ripped off https://stackoverflow.com/questions/18187714/android-open-target-blank-links-in-webview-with-external-browser
28
-            this.webView.webChromeClient = object : WebChromeClient() {
29
-                override fun onCreateWindow(
30
-                    view: WebView,
31
-                    dialog: Boolean,
32
-                    userGesture: Boolean,
33
-                    resultMsg: android.os.Message
34
-                ): Boolean {
35
-                    val result = view.hitTestResult
36
-                    val data = result.extra
37
-                    val context = view.context
38
-                    val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(data))
39
-                    context.startActivity(browserIntent)
40
-                    return false
41
-                }
42
-            }
43
-
44
-            webView.loadUrl("file:///android_asset/chat.html")
45
-        }
46
-
47
-    }

+ 2 - 52
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/SongsFragment.kt 파일 보기

@@ -5,62 +5,19 @@ import android.util.Log
5 5
 import android.view.LayoutInflater
6 6
 import android.view.View
7 7
 import android.view.ViewGroup
8
-import android.widget.TextView
9 8
 import androidx.fragment.app.Fragment
10 9
 import androidx.fragment.app.FragmentPagerAdapter
11
-import androidx.lifecycle.Observer
12
-import androidx.lifecycle.ViewModelProviders
13 10
 import androidx.viewpager.widget.ViewPager
14
-import com.google.android.material.snackbar.BaseTransientBottomBar
15
-import com.google.android.material.snackbar.Snackbar
16 11
 import com.google.android.material.tabs.TabLayout
17 12
 import fr.forum_thalie.tsumugi.R
18
-import fr.forum_thalie.tsumugi.preferenceStore
19 13
 import fr.forum_thalie.tsumugi.ui.songs.queuelp.LastPlayedFragment
20
-import fr.forum_thalie.tsumugi.ui.songs.queuelp.QueueFragment
21
-import fr.forum_thalie.tsumugi.ui.songs.request.FavoritesFragment
22
-import fr.forum_thalie.tsumugi.ui.songs.request.RequestFragment
23
-import fr.forum_thalie.tsumugi.ui.songs.request.Requestor
24 14
 
25 15
 class SongsFragment : Fragment() {
26 16
 
27 17
     private lateinit var adapter : SongsPagerAdapter
28
-    private lateinit var snackBar : Snackbar
29 18
     private lateinit var root: View
30 19
     private lateinit var viewPager: ViewPager
31 20
 
32
-
33
-    private val snackBarTextObserver: Observer<String?> = Observer {
34
-        if (Requestor.instance.snackBarText.value != "")
35
-        {
36
-            val snackBarLength = if (preferenceStore.getBoolean("snackbarPersistent", true))
37
-                Snackbar.LENGTH_INDEFINITE
38
-                else Snackbar.LENGTH_LONG
39
-            snackBar = Snackbar.make(viewPager, "", snackBarLength)
40
-
41
-            if (snackBarLength == Snackbar.LENGTH_INDEFINITE)
42
-            snackBar.setAction("OK") {
43
-                snackBar.dismiss()
44
-            }
45
-
46
-            snackBar.behavior = BaseTransientBottomBar.Behavior().apply {
47
-                setSwipeDirection(BaseTransientBottomBar.Behavior.SWIPE_DIRECTION_ANY)
48
-            }
49
-
50
-            val snackBarView = snackBar.view
51
-            val textView =
52
-                snackBarView.findViewById(com.google.android.material.R.id.snackbar_text) as TextView
53
-            if (Requestor.instance.addRequestMeta != "")
54
-                textView.maxLines = 4
55
-            else
56
-                textView.maxLines = 2
57
-            snackBar.setText((it as CharSequence))
58
-            snackBar.show()
59
-            Requestor.instance.snackBarText.value = "" // resetting afterwards to avoid re-triggering it when we enter again the fragment
60
-            Requestor.instance.addRequestMeta = ""
61
-        }
62
-    }
63
-
64 21
     override fun onCreateView(
65 22
         inflater: LayoutInflater,
66 23
         container: ViewGroup?,
@@ -71,9 +28,8 @@ class SongsFragment : Fragment() {
71 28
         viewPager = root.findViewById(R.id.tabPager)
72 29
         adapter = SongsPagerAdapter(childFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
73 30
         adapter.addFragment(LastPlayedFragment.newInstance(), "last played")
74
-        adapter.addFragment(QueueFragment.newInstance(), "queue")
75
-        adapter.addFragment(RequestFragment.newInstance(), "request")
76
-        adapter.addFragment(FavoritesFragment.newInstance(), "Favorites")
31
+
32
+        //adapter.addFragment(QueueFragment.newInstance(), "queue")
77 33
 
78 34
         viewPager.adapter = adapter
79 35
 
@@ -81,14 +37,8 @@ class SongsFragment : Fragment() {
81 37
         tabLayout.setupWithViewPager(viewPager)
82 38
         Log.d(tag, "SongFragment view created")
83 39
 
84
-        Requestor.instance.snackBarText.observeForever(snackBarTextObserver)
85 40
 
86 41
         return root
87 42
     }
88 43
 
89
-    override fun onDestroyView() {
90
-        Requestor.instance.snackBarText.removeObserver(snackBarTextObserver)
91
-        super.onDestroyView()
92
-    }
93
-
94 44
 }

+ 0 - 70
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/queuelp/QueueFragment.kt 파일 보기

@@ -1,70 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.queuelp
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.lifecycle.Observer
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.playerstore.PlayerStore
14
-import fr.forum_thalie.tsumugi.playerstore.Song
15
-
16
-class QueueFragment : Fragment(){
17
-    private val lastPlayedFragmentTag = this::class.java.name
18
-
19
-    private lateinit var recyclerView: RecyclerView
20
-    private lateinit var viewAdapter: RecyclerView.Adapter<*>
21
-    private lateinit var viewManager: RecyclerView.LayoutManager
22
-
23
-
24
-    private val queueObserver = Observer<Boolean> {
25
-        Log.d(tag, lastPlayedFragmentTag + "queue changed")
26
-        viewAdapter.notifyDataSetChanged()
27
-    }
28
-
29
-    override fun onCreateView(
30
-        inflater: LayoutInflater, container: ViewGroup?,
31
-        savedInstanceState: Bundle?
32
-    ): View? {
33
-        // Inflate the layout for this fragment
34
-        val root = inflater.inflate(R.layout.fragment_last_played, container, false)
35
-
36
-        viewManager = LinearLayoutManager(context)
37
-        viewAdapter = SongAdaptater(
38
-            if (PlayerStore.instance.queue.isEmpty())
39
-                ArrayList<Song>(listOf((Song("No queue - "))))
40
-            else
41
-                PlayerStore.instance.queue
42
-        )
43
-
44
-        recyclerView = root.findViewById<RecyclerView>(R.id.queue_lp_recycler).apply {
45
-            // use this setting to improve performance if you know that changes
46
-            // in content do not change the layout size of the RecyclerView
47
-            setHasFixedSize(true)
48
-
49
-            // use a linear layout manager
50
-            layoutManager = viewManager
51
-
52
-            // specify an viewAdapter (see also next example)
53
-            adapter = viewAdapter
54
-        }
55
-
56
-        PlayerStore.instance.isQueueUpdated.observeForever(queueObserver)
57
-
58
-        return root
59
-    }
60
-
61
-    override fun onDestroyView() {
62
-        PlayerStore.instance.isQueueUpdated.removeObserver(queueObserver)
63
-        super.onDestroyView()
64
-    }
65
-
66
-    companion object {
67
-        @JvmStatic
68
-        fun newInstance() = QueueFragment()
69
-    }
70
-}

+ 0 - 69
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/CooldownCalculator.kt 파일 보기

@@ -1,69 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
2
-
3
-import kotlin.math.exp
4
-import kotlin.math.max
5
-
6
-/*
7
-//PHP cooldown calculator
8
-
9
-function pretty_cooldown($lp, $lr, $rc) {
10
-	$delay = delay($rc);
11
-	$now = time();
12
-	$cd = intval(max($lp + $delay - $now, $lr + $delay - $now));
13
-	if ($cd <= 0)
14
-		return "Request";
15
-	$days = intdiv_1($cd, 86400);
16
-	$cd = $cd % 86400;
17
-	$hours = intdiv_1($cd, 3600);
18
-	$cd = $cd % 3600;
19
-	$minutes = intdiv_1($cd, 60);
20
-	$seconds = $cd % 60;
21
-	if ($days > 0)
22
-		return "Requestable in ".$days."d".$hours."h";
23
-	else if ($hours > 0)
24
-		return "Requestable in ".$hours."h".$minutes."m";
25
-	else if ($minutes > 0)
26
-		return "Requestable in ".$minutes."m".$seconds."s";
27
-	return "Request";
28
-}
29
-function requestable($lastplayed, $requests) {
30
-	$delay = delay($requests);
31
-	return (time() - $lastplayed) > $delay;
32
-}
33
-function delay($priority) {
34
-	// priority is 30 max
35
-		if ($priority > 30)
36
-			$priority = 30;
37
-		// between 0 and 7 return magic
38
-		if ($priority >= 0 and $priority <= 7)
39
-			$cd = -11057 * $priority * $priority + 172954 * $priority + 81720;
40
-		// if above that, return magic crazy numbers
41
-		else
42
-			$cd = (int) (599955 * exp(0.0372 * $priority) + 0.5);
43
-		return $cd / 2;
44
-}
45
- */
46
-
47
-// this function implements the magic delay used on R/a/dio website:
48
-// https://github.com/R-a-dio/site/blob/develop/app/start/global.php#L125
49
-// (Seriously guys, what were you thinking with these crazy magic numbers...)
50
-fun delay(rawPriority: Int) : Int {
51
-    val priority = if (rawPriority > 30) 30 else rawPriority
52
-    val coolDown : Int =
53
-        if (priority in 0..7)
54
-            -11057 * priority * priority + 172954 * priority + 81720
55
-        else
56
-            (599955 * exp(0.0372 * (priority.toDouble()) + 0.5)).toInt()
57
-    return coolDown/2
58
-}
59
-
60
-// I tweaked this to report in a single point whether the song is requestable or not
61
-fun coolDown(lastPlayed: Int?, lastRequest: Int?, requestsNbr: Int?) : Long {
62
-    if (requestsNbr == null || lastPlayed == null || lastRequest == null)
63
-        return Long.MAX_VALUE // maximum positive value : the song won't be requestable
64
-
65
-    val delay = delay(requestsNbr)
66
-    val now = (System.currentTimeMillis() / 1000)
67
-    return max(lastPlayed, lastRequest) + delay - now
68
-    // if coolDown < 0, the song is requestable.
69
-}

+ 0 - 132
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/FavoritesFragment.kt 파일 보기

@@ -1,132 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
2
-
3
-import android.os.Build
4
-import android.os.Bundle
5
-import android.util.Log
6
-import android.view.LayoutInflater
7
-import android.view.View
8
-import android.view.ViewGroup
9
-import android.widget.SearchView
10
-import android.widget.TextView
11
-import androidx.appcompat.widget.AppCompatButton
12
-import androidx.fragment.app.Fragment
13
-import androidx.lifecycle.Observer
14
-import androidx.recyclerview.widget.LinearLayoutManager
15
-import androidx.recyclerview.widget.RecyclerView
16
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
17
-import fr.forum_thalie.tsumugi.*
18
-
19
-
20
-class FavoritesFragment : Fragment()  {
21
-
22
-    private lateinit var recyclerView: RecyclerView
23
-    private lateinit var viewAdapter: RecyclerView.Adapter<*>
24
-    private lateinit var viewManager: RecyclerView.LayoutManager
25
-    private lateinit var searchView: SearchView
26
-    private lateinit var root: View
27
-    private lateinit var recyclerSwipe: SwipeRefreshLayout
28
-
29
-    private val favoritesSongObserver : Observer<Boolean> = Observer {
30
-        viewAdapter.notifyDataSetChanged()
31
-        createView(isCallback = true) // force-re-create the view, but do not call again the initFavorites (avoid callback loop)
32
-        recyclerSwipe.isRefreshing = false // disable refreshing animation. Needs to be done manually...
33
-    }
34
-
35
-    override fun onCreateView(
36
-        inflater: LayoutInflater,
37
-        container: ViewGroup?,
38
-        savedInstanceState: Bundle?
39
-    ): View? {
40
-        super.onCreateView(inflater, container, savedInstanceState)
41
-        root = inflater.inflate(R.layout.fragment_request, container, false)
42
-
43
-        return createView()
44
-    }
45
-
46
-    private fun createView(isCallback: Boolean = false) : View?
47
-    {
48
-
49
-        viewAdapter = RequestSongAdapter(Requestor.instance.favoritesSongArray)
50
-
51
-        val listener : SearchView.OnQueryTextListener = object : SearchView.OnQueryTextListener{
52
-            override fun onQueryTextSubmit(query: String?): Boolean {
53
-                // do nothing
54
-                return true
55
-            }
56
-            override fun onQueryTextChange(newText: String?): Boolean {
57
-                (viewAdapter as RequestSongAdapter).filter(newText ?: "")
58
-                return true
59
-            }
60
-        }
61
-
62
-        searchView = root.findViewById(R.id.searchBox)
63
-        searchView.queryHint = "Search filter..."
64
-        searchView.setOnQueryTextListener(listener)
65
-        viewManager = LinearLayoutManager(context)
66
-        recyclerView = root.findViewById<RecyclerView>(R.id.request_recycler).apply {
67
-            // use this setting to improve performance if you know that changes
68
-            // in content do not change the layout size of the RecyclerView
69
-            setHasFixedSize(true)
70
-
71
-            // use a linear layout manager
72
-            layoutManager = viewManager
73
-
74
-            // specify an viewAdapter (see also next example)
75
-            adapter = viewAdapter
76
-        }
77
-
78
-        val noUserNameText : TextView = root.findViewById(R.id.noUserNameText)
79
-
80
-        recyclerSwipe = root.findViewById(R.id.recyclerSwipe) as SwipeRefreshLayout
81
-        recyclerSwipe.setOnRefreshListener {
82
-            val userName1 = preferenceStore.getString("userName", null)
83
-            Log.d(tag,"userName = $userName1")
84
-            if (userName1 != null && !userName1.isBlank())
85
-            {
86
-                noUserNameText.visibility = View.GONE
87
-                Requestor.instance.initFavorites()
88
-            } else {
89
-                noUserNameText.visibility = View.VISIBLE
90
-                recyclerSwipe.isRefreshing = false
91
-            }
92
-        }
93
-
94
-
95
-        val userName1 = preferenceStore.getString("userName", null)
96
-        Log.d(tag,"userName = $userName1")
97
-        if (userName1 != null && !userName1.isBlank())
98
-        {
99
-            noUserNameText.visibility = View.GONE
100
-            if (!isCallback) // avoid callback loop if called from the Observer.
101
-                Requestor.instance.initFavorites()
102
-        } else {
103
-            noUserNameText.visibility = View.VISIBLE
104
-            recyclerSwipe.isRefreshing = false
105
-        }
106
-
107
-        val raFButton : AppCompatButton = root.findViewById(R.id.ra_f_button)
108
-
109
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) // for API21+ Material Design makes ripples on the button.
110
-            raFButton.supportBackgroundTintList = colorGreenList
111
-        else // But on API20- no Material Design support, so we add some more color when clicked
112
-            raFButton.supportBackgroundTintList = colorGreenListCompat
113
-        raFButton.isEnabled = true
114
-        raFButton.isClickable = true
115
-        raFButton.setOnClickListener {
116
-            val s  = Requestor.instance.raF()
117
-            Requestor.instance.snackBarText.value = ""
118
-            Requestor.instance.addRequestMeta = "Request: ${s.artist.value} - ${s.title.value}\n"
119
-            Requestor.instance.request(s.id)
120
-        }
121
-        raFButton.visibility = View.VISIBLE
122
-
123
-        Requestor.instance.isFavoritesUpdated.observe(viewLifecycleOwner, favoritesSongObserver)
124
-        return root
125
-    }
126
-
127
-
128
-    companion object {
129
-        @JvmStatic
130
-        fun newInstance() = FavoritesFragment()
131
-    }
132
-}

+ 0 - 81
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestFragment.kt 파일 보기

@@ -1,81 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
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 android.widget.SearchView
9
-import androidx.fragment.app.Fragment
10
-import androidx.lifecycle.Observer
11
-import androidx.recyclerview.widget.LinearLayoutManager
12
-import androidx.recyclerview.widget.RecyclerView
13
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
14
-import fr.forum_thalie.tsumugi.R
15
-
16
-class RequestFragment : Fragment() {
17
-
18
-    private lateinit var recyclerView: RecyclerView
19
-    private lateinit var viewAdapter: RecyclerView.Adapter<*>
20
-    private lateinit var viewManager: RecyclerView.LayoutManager
21
-    private lateinit var searchView: SearchView
22
-
23
-    private val listener : SearchView.OnQueryTextListener = object : SearchView.OnQueryTextListener{
24
-        override fun onQueryTextSubmit(query: String?): Boolean {
25
-            if (query == null || query.isEmpty())
26
-                Requestor.instance.snackBarText.value = "Field is empty, no search possible."
27
-            else
28
-                Requestor.instance.search(query)
29
-            return true
30
-        }
31
-        override fun onQueryTextChange(newText: String?): Boolean {
32
-            if (newText == "")
33
-            {
34
-                Requestor.instance.reset()
35
-                viewAdapter.notifyDataSetChanged() // this is to remove the "Load more" button
36
-            }
37
-            return true
38
-        }
39
-    }
40
-
41
-    private val requestSongObserver = Observer<Boolean> {
42
-        Log.d(tag, "request song list changed")
43
-        viewAdapter.notifyDataSetChanged()
44
-    }
45
-
46
-    override fun onCreateView(
47
-        inflater: LayoutInflater, container: ViewGroup?,
48
-        savedInstanceState: Bundle?
49
-    ): View? {
50
-        // Inflate the layout for this fragment
51
-        val root = inflater.inflate(R.layout.fragment_request, container, false)
52
-
53
-        val recyclerSwipe = root.findViewById(R.id.recyclerSwipe) as SwipeRefreshLayout
54
-        recyclerSwipe.isEnabled = false // don't need to pull-to-refresh for Request
55
-
56
-        searchView = root.findViewById(R.id.searchBox)
57
-        searchView.setOnQueryTextListener(listener)
58
-
59
-        viewManager = LinearLayoutManager(context)
60
-        viewAdapter = RequestSongAdapter(Requestor.instance.requestSongArray)
61
-
62
-        recyclerView = root.findViewById<RecyclerView>(R.id.request_recycler).apply {
63
-            // use this setting to improve performance if you know that changes
64
-            // in content do not change the layout size of the RecyclerView
65
-            setHasFixedSize(true)
66
-
67
-            // use a linear layout manager
68
-            layoutManager = viewManager
69
-
70
-            // specify an viewAdapter (see also next example)
71
-            adapter = viewAdapter
72
-        }
73
-        Requestor.instance.isRequestResultUpdated.observe(viewLifecycleOwner, requestSongObserver)
74
-        return root
75
-    }
76
-
77
-    companion object {
78
-        @JvmStatic
79
-        fun newInstance() = RequestFragment()
80
-    }
81
-}

+ 0 - 42
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestResponse.kt 파일 보기

@@ -1,42 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
2
-
3
-import fr.forum_thalie.tsumugi.playerstore.Song
4
-import org.json.JSONObject
5
-
6
-class RequestResponse(jsonResponse: JSONObject) {
7
-    //val total: Int = jsonResponse.getInt("total")
8
-    //val perPage: Int = jsonResponse.getInt("per_page") // should stay 20 but in any case...
9
-    val currentPage: Int = jsonResponse.getInt("current_page")
10
-    val lastPage: Int = jsonResponse.getInt("last_page")
11
-    //val fromNbr: Int = jsonResponse.getInt("from")
12
-    //val toNbr: Int = jsonResponse.getInt("to")
13
-    var songs : ArrayList<Song> = ArrayList()
14
-
15
-    init {
16
-        val songList = jsonResponse.getJSONArray("data")
17
-        for (i in 0 until songList.length())
18
-        {
19
-
20
-            val title = (songList[i] as JSONObject).getString("title")
21
-            val artist = (songList[i] as JSONObject).getString("artist")
22
-            val id = (songList[i] as JSONObject).getInt("id")
23
-
24
-            val s = Song("", id)
25
-            s.title.value = title
26
-            s.artist.value = artist
27
-            s.isRequestable = (songList[i] as JSONObject).getBoolean("requestable")
28
-            // TODO add the time before being requestable.
29
-            songs.add(s)
30
-        }
31
-    }
32
-
33
-    override fun toString(): String {
34
-        var s = ""
35
-        for (i in 0 until songs.size)
36
-        {
37
-            s += (songs[i].artist.value + " - " + songs[i].title.value + " | ")
38
-        }
39
-        return s
40
-    }
41
-
42
-}

+ 0 - 148
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/RequestSongAdapter.kt 파일 보기

@@ -1,148 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
2
-
3
-import android.annotation.SuppressLint
4
-import android.os.Build
5
-import android.util.TypedValue
6
-import android.view.LayoutInflater
7
-import android.view.ViewGroup
8
-import android.widget.TextView
9
-import androidx.constraintlayout.widget.ConstraintLayout
10
-import androidx.core.widget.TextViewCompat
11
-import androidx.recyclerview.widget.RecyclerView
12
-import fr.forum_thalie.tsumugi.*
13
-import fr.forum_thalie.tsumugi.playerstore.Song
14
-import kotlinx.android.synthetic.main.request_song_view.view.*
15
-import android.view.View
16
-import kotlinx.android.synthetic.main.button_load_more.view.*
17
-import android.R.attr.name
18
-import android.text.method.TextKeyListener.clear
19
-import android.util.Log
20
-import java.util.*
21
-import kotlin.collections.ArrayList
22
-
23
-
24
-class RequestSongAdapter(private val dataSet: ArrayList<Song>
25
-    /*,
26
-    context: Context,
27
-    resource: Int,
28
-    objects: Array<out Song>*/
29
-) : RecyclerView.Adapter<RequestSongAdapter.MyViewHolder>() /*ArrayAdapter<Song>(context, resource, objects)*/ {
30
-
31
-    private val viewTypeCell = 1 // normal cell with song and request button
32
-    private val viewTypeFooter = 2 // the bottom cell should be the "load more" button whenever needed
33
-
34
-    // Provide a reference to the views for each data item
35
-    // Complex data items may need more than one view per item, and
36
-    // you provide access to all the views for a data item in a view holder.
37
-    // Each data item is just a string in this case that is shown in a TextView.
38
-    class MyViewHolder(view: ConstraintLayout) : RecyclerView.ViewHolder(view)
39
-
40
-
41
-    // Create new views (invoked by the layout manager)
42
-    override fun onCreateViewHolder(parent: ViewGroup,
43
-                                    viewType: Int): MyViewHolder {
44
-        // create a new view
45
-
46
-        val view =
47
-            if (viewType == viewTypeCell)
48
-                LayoutInflater.from(parent.context).inflate(R.layout.request_song_view, parent, false) as ConstraintLayout
49
-            else
50
-                LayoutInflater.from(parent.context).inflate(R.layout.button_load_more, parent, false) as ConstraintLayout
51
-
52
-        // set the view's size, margins, paddings and layout parameters
53
-        //...
54
-        return MyViewHolder(view)
55
-    }
56
-
57
-    // Replace the contents of a view (invoked by the layout manager)
58
-    @SuppressLint("SetTextI18n")
59
-    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
60
-
61
-        if (itemCount <= 1)
62
-        {
63
-            // in any case, if there's nothing, don't display the loadMore button!!
64
-            holder.itemView.loadMoreButton.visibility = View.GONE
65
-            return
66
-        }
67
-
68
-        if (holder.itemViewType == viewTypeFooter)
69
-        {
70
-            if (Requestor.instance.isLoadMoreVisible)
71
-                holder.itemView.loadMoreButton.visibility = View.VISIBLE
72
-            else
73
-                holder.itemView.loadMoreButton.visibility = View.GONE
74
-            holder.itemView.loadMoreButton.text = "Load more results"
75
-            holder.itemView.loadMoreButton.setOnClickListener{
76
-                Requestor.instance.loadMore()
77
-            }
78
-            return
79
-        }
80
-
81
-        val artist = holder.itemView.findViewById<TextView>(R.id.request_song_artist)
82
-        val title = holder.itemView.findViewById<TextView>(R.id.request_song_title)
83
-        val button = holder.itemView.request_button
84
-
85
-        if (dataSet[position].isRequestable) {
86
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) // for API21+ Material Design makes ripples on the button.
87
-                button.supportBackgroundTintList = colorGreenList
88
-            else // But on API20- no Material Design support, so we add some more color when clicked
89
-                button.supportBackgroundTintList = colorGreenListCompat
90
-            button.isEnabled = true
91
-            button.isClickable = true
92
-            button.setOnClickListener {
93
-                Requestor.instance.request(dataSet[position].id)
94
-            }
95
-        } else {
96
-            button.supportBackgroundTintList = colorRedList
97
-            button.isEnabled = false
98
-            button.isClickable = false
99
-        }
100
-
101
-        TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
102
-            button,2, 24, 2, TypedValue.COMPLEX_UNIT_SP)
103
-        artist.text = dataSet[position].artist.value
104
-        title.text = dataSet[position].title.value
105
-    }
106
-
107
-    // Return the size of your dataset (invoked by the layout manager)
108
-    override fun getItemCount() = dataSet.size + 1 // add 1 for the "Load more results" button
109
-
110
-    override fun getItemViewType(position: Int): Int {
111
-        return if (position == dataSet.size) viewTypeFooter else viewTypeCell
112
-    }
113
-
114
-    /*
115
-    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
116
-        // create a new view
117
-        val view = LayoutInflater.from(parent.context)
118
-            .inflate(R.layout.song_view, parent, false) as ConstraintLayout
119
-    }
120
-    */
121
-
122
-    // a filtering function. As naive as it could be, but it should work.
123
-    private val dataSetOrig = ArrayList<Song>()
124
-    init {
125
-        dataSetOrig.addAll(dataSet)
126
-    }
127
-
128
-    fun filter(entry: String) {
129
-        var text = entry
130
-        dataSet.clear()
131
-        Log.d(tag, "entering filter")
132
-        if (text.isEmpty()) {
133
-            dataSet.addAll(dataSetOrig)
134
-        } else {
135
-            text = text.toLowerCase(locale = Locale.ROOT)
136
-            for (item in dataSetOrig) {
137
-                Log.d(tag, "$text, ${item.artist.value!!.toLowerCase(locale = Locale.ROOT)}, ${item.title.value!!.toLowerCase(locale = Locale.ROOT)}")
138
-                if (item.artist.value!!.toLowerCase(locale = Locale.ROOT).contains(text) ||
139
-                    item.title.value!!.toLowerCase(locale = Locale.ROOT).contains(text)) {
140
-                    dataSet.add(item)
141
-                }
142
-            }
143
-        }
144
-        notifyDataSetChanged()
145
-    }
146
-
147
-}
148
-

+ 0 - 301
app/src/main/java/fr/forum_thalie/tsumugi/ui/songs/request/Requestor.kt 파일 보기

@@ -1,301 +0,0 @@
1
-package fr.forum_thalie.tsumugi.ui.songs.request
2
-
3
-import android.util.Log
4
-import androidx.lifecycle.MutableLiveData
5
-import fr.forum_thalie.tsumugi.ActionOnError
6
-import fr.forum_thalie.tsumugi.Async
7
-import fr.forum_thalie.tsumugi.playerstore.Song
8
-import fr.forum_thalie.tsumugi.preferenceStore
9
-import fr.forum_thalie.tsumugi.tag
10
-import org.json.JSONArray
11
-import org.json.JSONException
12
-import org.json.JSONObject
13
-import java.io.BufferedReader
14
-import java.io.IOException
15
-import java.io.InputStreamReader
16
-import java.net.CookieHandler
17
-import java.net.CookieManager
18
-import java.net.MalformedURLException
19
-import java.net.URL
20
-import java.util.*
21
-import java.util.regex.Pattern
22
-
23
-import javax.net.ssl.HttpsURLConnection
24
-import kotlin.collections.ArrayList
25
-import kotlin.random.Random
26
-
27
-/**
28
- * Requests a song via the website's API
29
- *
30
- * We scrape the website for a CSRF token and POST it to /request/ endpoint with
31
- * the song id
32
- *
33
- * Created by Kethsar on 1/2/2017.
34
- * Converted to Kotlin and adapted by Yattoz on 05 Nov. 2019
35
- */
36
-
37
-class Requestor {
38
-    var addRequestMeta: String = ""
39
-    private val cookieManager: CookieManager = CookieManager()
40
-    private val requestUrl = "https://r-a-d.io/request/%1\$d"
41
-    private val searchUrl = "https://r-a-d.io/api/search/%1s?page=%2\$d"
42
-    private val favoritesUrl = "https://r-a-d.io/faves/%1s?dl=true"
43
-    private val songThresholdStep = 50
44
-    private var songThreshold = songThresholdStep
45
-    private var localQuery = ""
46
-
47
-    private var token: String? = null
48
-    val snackBarText : MutableLiveData<String?> = MutableLiveData()
49
-    private var responseArray : ArrayList<RequestResponse> = ArrayList()
50
-    val requestSongArray : ArrayList<Song> = ArrayList()
51
-    val favoritesSongArray : ArrayList<Song> = ArrayList()
52
-    val isRequestResultUpdated : MutableLiveData<Boolean> = MutableLiveData()
53
-    val isFavoritesUpdated : MutableLiveData<Boolean> = MutableLiveData()
54
-    var isLoadMoreVisible: Boolean = false
55
-
56
-
57
-    init {
58
-        snackBarText.value = ""
59
-        isRequestResultUpdated.value = false
60
-        isFavoritesUpdated.value = false
61
-        isLoadMoreVisible = false
62
-    }
63
-
64
-    fun initFavorites(userName : String? = preferenceStore.getString("userName", null)){
65
-        Log.d(tag, "initializing favorites")
66
-        favoritesSongArray.clear()
67
-        if (userName == null)
68
-        {
69
-            // Display is done by default in the XML.
70
-            Log.d(tag, "no user name set for favorites")
71
-            isFavoritesUpdated.value = true
72
-            return
73
-        }
74
-        val favoritesUserUrl = String.format(Locale.getDefault(), favoritesUrl, userName)
75
-        val scrapeFavorites : (Any?) -> JSONArray = {
76
-            JSONArray(URL(favoritesUserUrl).readText())
77
-        }
78
-        val postFavorites :  (Any?) -> Unit = {
79
-            val res = it as JSONArray
80
-            for (i in 0 until (res).length())
81
-            {
82
-                val item = res.getJSONObject(i)
83
-                val artistTitle = item.getString("meta")
84
-                val id : Int? = if (item.isNull("tracks_id"))
85
-                    null
86
-                else
87
-                    item.getInt("tracks_id")
88
-
89
-                val lastRequested : Int? = if (item.isNull("lastrequested")) null else item.getInt("lastrequested")
90
-                val lastPlayed : Int? = if (item.isNull("lastplayed")) null else item.getInt("lastplayed")
91
-                val requestCount : Int? = if (item.isNull("requestcount")) null else item.getInt("requestcount")
92
-                val isRequestable = (coolDown(lastPlayed, lastRequested, requestCount) < 0)
93
-                //Log.d(tag, "val : $id")
94
-                favoritesSongArray.add(Song(artistTitle, id ?: 0, isRequestable))
95
-            }
96
-            Log.d(tag, "favorites : $favoritesSongArray")
97
-            isFavoritesUpdated.value = true
98
-        }
99
-        Async(scrapeFavorites, postFavorites, ActionOnError.NOTIFY)
100
-    }
101
-
102
-    fun search(query: String)
103
-    {
104
-        responseArray.clear()
105
-        requestSongArray.clear()
106
-        localQuery = query
107
-        searchPage(query, 1) // the searchPage function is recursive to get all pages.
108
-    }
109
-
110
-    private fun searchPage(query: String, pageNumber : Int)
111
-    {
112
-        val searchURL = String.format(Locale.getDefault(), searchUrl, query, pageNumber)
113
-        val scrape : (Any?) -> JSONObject = {
114
-            val res = URL(searchURL).readText()
115
-            val json = JSONObject(res)
116
-            json
117
-        }
118
-        val post : (Any?) -> Unit = {
119
-            val response = RequestResponse(it as JSONObject)
120
-
121
-            responseArray.add(response)
122
-            for (i in 0 until response.songs.size)
123
-            {
124
-                requestSongArray.add(response.songs[i])
125
-            }
126
-            isRequestResultUpdated.value = true
127
-            if (requestSongArray.size >= songThreshold)
128
-            {
129
-                isLoadMoreVisible = true
130
-
131
-            } else {
132
-                if (response.currentPage < response.lastPage)
133
-                    searchPage(query, pageNumber + 1) // recursive call to get the next page
134
-                else
135
-                    finishSearch()
136
-            }
137
-
138
-        }
139
-        Async(scrape, post, ActionOnError.NOTIFY)
140
-    }
141
-
142
-    private fun finishSearch()
143
-    {
144
-        isLoadMoreVisible = false
145
-    }
146
-
147
-    fun reset()
148
-    {
149
-        requestSongArray.clear()
150
-        responseArray.clear()
151
-        isRequestResultUpdated.value = false
152
-        songThreshold = songThresholdStep
153
-    }
154
-
155
-    fun loadMore()
156
-    {
157
-        songThreshold += songThresholdStep
158
-        searchPage(localQuery, responseArray.last().currentPage + 1)
159
-    }
160
-
161
-
162
-    /**
163
-     * Scrape the website for the CSRF token required for requesting
164
-     * scrapeToken and postToken are the two lambas run by the Async() class.
165
-     */
166
-
167
-    private val scrapeToken : (Any?) -> Any? = {
168
-        val radioSearchUrl = "https://r-a-d.io/search"
169
-        var searchURL: URL? = null
170
-        var retVal: String? = null
171
-        var reader: BufferedReader? = null
172
-
173
-        CookieHandler.setDefault(cookieManager) // it[0] ??
174
-
175
-        try {
176
-            searchURL = URL(radioSearchUrl)
177
-        } catch (e: MalformedURLException) {
178
-            e.printStackTrace()
179
-        }
180
-
181
-        try {
182
-            reader = BufferedReader(InputStreamReader(searchURL!!.openStream(), "UTF-8"))
183
-            var line: String?
184
-            line = reader.readLine()
185
-            while (line != null)
186
-            {
187
-                line = line.trim { it <= ' ' }
188
-                val p = Pattern.compile("value=\"(\\w+)\"")
189
-                val m = p.matcher(line)
190
-
191
-                if (line.startsWith("<form")) {
192
-                    if (m.find()) {
193
-                        retVal = m.group(1)
194
-                        break
195
-                    }
196
-                }
197
-                line = reader.readLine()
198
-            }
199
-        } catch (e: IOException) {
200
-            e.printStackTrace()
201
-        } finally {
202
-            if (reader != null) try {
203
-                reader.close()
204
-            } catch (ignored: IOException) {
205
-            }
206
-
207
-        }
208
-        retVal
209
-    }
210
-
211
-    private val postToken : (Any?) -> (Unit) = {
212
-        token = it as String?
213
-    }
214
-
215
-    /**
216
-     * Request the song with the CSRF token that was scraped
217
-     */
218
-    private val requestSong: (Any?) -> Any? = {
219
-        val reqString = it as String
220
-        var response = ""
221
-
222
-        try {
223
-            val reqURL = URL(reqString)
224
-            val conn = reqURL.openConnection() as HttpsURLConnection
225
-            val tokenObject = JSONObject()
226
-
227
-            tokenObject.put("_token", token)
228
-            val requestBytes = tokenObject.toString().toByteArray()
229
-
230
-            conn.requestMethod = "POST"
231
-            conn.doOutput = true
232
-            conn.doInput = true
233
-            conn.setChunkedStreamingMode(0)
234
-            conn.setRequestProperty("Content-Type", "application/json")
235
-
236
-            val os = conn.outputStream
237
-            os.write(requestBytes)
238
-
239
-            val responseCode = conn.responseCode
240
-
241
-            if (responseCode == HttpsURLConnection.HTTP_OK) {
242
-                var line: String?
243
-                val br = BufferedReader(InputStreamReader(
244
-                    conn.inputStream))
245
-                line = br.readLine()
246
-                while (line != null) {
247
-                    response += line
248
-                    line = br.readLine()
249
-                }
250
-            } else {
251
-                response += ""
252
-            }
253
-        } catch (ex: IOException) {
254
-            ex.printStackTrace()
255
-        } catch (ex: JSONException) {
256
-            ex.printStackTrace()
257
-        }
258
-
259
-        response
260
-    }
261
-
262
-    private val postSong  : (Any?) -> (Unit) = {
263
-        val response = JSONObject(it as String)
264
-        val key = response.names()!!.get(0) as String
265
-        val value = response.getString(key)
266
-
267
-        snackBarText.postValue(addRequestMeta + value)
268
-    }
269
-
270
-
271
-    fun request(songID: Int?) {
272
-        val requestSongUrl = String.format(requestUrl, songID!!)
273
-        if (token == null) {
274
-            Async(scrapeToken, postToken, ActionOnError.NOTIFY)
275
-        }
276
-        Async(requestSong, postSong, ActionOnError.NOTIFY, requestSongUrl)
277
-    }
278
-
279
-    fun raF() : Song {
280
-        // request a random favorite song. HELL YEAH
281
-        val requestableSongArray = ArrayList<Song>()
282
-        for (i in 0 until favoritesSongArray.size)
283
-        {
284
-            if (favoritesSongArray[i].isRequestable && (favoritesSongArray[i].id ?: 0) > 0)
285
-                requestableSongArray.add(favoritesSongArray[i])
286
-        }
287
-        return if (requestableSongArray.isNotEmpty()) {
288
-            val songNbr =  Random(System.currentTimeMillis()).nextInt(1, requestableSongArray.size)
289
-            requestableSongArray[songNbr]
290
-        } else {
291
-            Song("No song requestable - ")
292
-        }
293
-    }
294
-
295
-    companion object {
296
-        val instance by lazy {
297
-            Requestor()
298
-        }
299
-    }
300
-
301
-}

+ 0 - 13
app/src/main/res/drawable-anydpi-v24/lollipop_logo.xml 파일 보기

@@ -1,13 +0,0 @@
1
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
2
-    android:width="24dp"
3
-    android:height="24dp"
4
-    android:viewportWidth="26.086956"
5
-    android:viewportHeight="26.086956"
6
-    android:tint="#FFFFFF">
7
-  <group android:translateX="1.0434783"
8
-      android:translateY="1.0434783">
9
-      <path
10
-          android:fillColor="#FF000000"
11
-          android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2L8.3,6l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM20,12h-2v-2h-2v2L4,12L4,8h16v4z"/>
12
-  </group>
13
-</vector>

+ 0 - 11
app/src/main/res/drawable-anydpi/ic_chat_processing.xml 파일 보기

@@ -1,11 +0,0 @@
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="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM9,11L7,11L7,9h2v2zM13,11h-2L11,9h2v2zM17,11h-2L15,9h2v2z"/>
11
-</vector>

+ 0 - 11
app/src/main/res/drawable-anydpi/ic_open_in_browser.xml 파일 보기

@@ -1,11 +0,0 @@
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,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2L5,18L5,8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.89,-2 -2,-2zM12,10l-4,4h3v6h2v-6h3l-4,-4z"/>
11
-</vector>

BIN
app/src/main/res/drawable-hdpi/ic_chat_processing.png 파일 보기


BIN
app/src/main/res/drawable-hdpi/ic_open_in_browser.png 파일 보기


BIN
app/src/main/res/drawable-hdpi/lollipop_logo.png 파일 보기


BIN
app/src/main/res/drawable-mdpi/ic_chat_processing.png 파일 보기


BIN
app/src/main/res/drawable-mdpi/ic_open_in_browser.png 파일 보기


BIN
app/src/main/res/drawable-mdpi/lollipop_logo.png 파일 보기


+ 2 - 2
app/src/main/res/drawable-v23/launch_screen.xml 파일 보기

@@ -1,11 +1,11 @@
1 1
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
2 2
     android:opacity="opaque">
3 3
     <!-- The background color, preferably the same as your normal theme -->
4
-    <item android:drawable="@color/colorPrimaryDark"/>
4
+    <item android:drawable="@color/colorAccent"/>
5 5
     <!-- Your product logo - 144dp color version of your app icon -->
6 6
 
7 7
     <item
8
-        android:drawable="@drawable/ic_radio_logo_round"
8
+        android:drawable="@drawable/ic_tsumugi_lineart"
9 9
         android:gravity="center"
10 10
         android:height="144dp"
11 11
         android:width="144dp"

BIN
app/src/main/res/drawable-xhdpi/ic_chat_processing.png 파일 보기


BIN
app/src/main/res/drawable-xhdpi/ic_open_in_browser.png 파일 보기


BIN
app/src/main/res/drawable-xhdpi/lollipop_logo.png 파일 보기


BIN
app/src/main/res/drawable-xxhdpi/ic_chat_processing.png 파일 보기


BIN
app/src/main/res/drawable-xxhdpi/ic_open_in_browser.png 파일 보기


BIN
app/src/main/res/drawable-xxhdpi/lollipop_logo.png 파일 보기


BIN
app/src/main/res/drawable-xxxhdpi/lollipop_logo.png 파일 보기


BIN
app/src/main/res/drawable/actionbar_logo.jpg 파일 보기


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 42 - 0
app/src/main/res/drawable/ic_launcher_foreground.xml


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 18
app/src/main/res/drawable/ic_radio_logo_round.xml


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 18
app/src/main/res/drawable/ic_radiologo.xml


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 59 - 0
app/src/main/res/drawable/ic_tsumugi_lineart.xml


BIN
app/src/main/res/drawable/logo_roundsquare.png 파일 보기


BIN
app/src/main/res/drawable/normal_logo.png 파일 보기


+ 0 - 42
app/src/main/res/drawable/seek_bar_progress.xml 파일 보기

@@ -1,42 +0,0 @@
1
-<?xml version="1.0" encoding="utf-8"?>
2
-<!-- shamelessly ripped off https://stackoverflow.com/questions/32212259/android-how-to-change-default-seekbar-thickness -->
3
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4
-
5
-    <item android:id="@android:id/background">
6
-        <shape>
7
-
8
-            <corners
9
-                android:radius="4dp"
10
-                />
11
-            <solid
12
-                android:color="@color/seek_bar_background"
13
-                />
14
-        </shape>
15
-    </item>
16
-
17
-    <item android:id="@android:id/secondaryProgress">
18
-        <clip>
19
-            <shape>
20
-                <corners
21
-                    android:radius="4dp"
22
-                    />
23
-                <solid
24
-                    android:color="@color/seek_bar_secondary_progress"
25
-                    />
26
-            </shape>
27
-        </clip>
28
-    </item>
29
-
30
-    <item android:id="@android:id/progress">
31
-        <clip>
32
-            <shape>
33
-                <corners
34
-                    android:radius="4dp"
35
-                    />
36
-                <solid
37
-                    android:color="@color/seek_bar_progress"
38
-                    />
39
-            </shape>
40
-        </clip>
41
-    </item>
42
-</layer-list>

+ 0 - 25
app/src/main/res/drawable/seek_bar_thumb.xml 파일 보기

@@ -1,25 +0,0 @@
1
-<?xml version="1.0" encoding="utf-8"?>
2
-<!-- shamelessly ripped off https://stackoverflow.com/questions/32212259/android-how-to-change-default-seekbar-thickness -->
3
-<!--
4
-    <shape xmlns:android="http://schemas.android.com/apk/res/android"
5
-        android:shape="oval"
6
-        android:innerRadius="@dimen/seek_bar_thumb_size">
7
-        <size
8
-            android:height="@dimen/seek_bar_thumb_size"
9
-            android:width="@dimen/seek_bar_thumb_size"
10
-            />
11
-        <solid android:color="@color/seek_bar_progress"/>
12
-    </shape>
13
--->
14
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
15
-    android:shape="rectangle"
16
-    >
17
-    <corners
18
-        android:radius="2dp"
19
-        />
20
-    <size
21
-        android:height="@dimen/seek_bar_thumb_height"
22
-        android:width="@dimen/seek_bar_thumb_size"
23
-        />
24
-    <solid android:color="@color/whited2"/>
25
-</shape>

+ 5 - 3
app/src/main/res/layout/activity_main.xml 파일 보기

@@ -37,15 +37,17 @@
37 37
     app:layout_constraintTop_toTopOf="parent"
38 38
     >
39 39
 
40
+    <!-- REMOVE VISIBILITY GONE TO ADD AN ICON IN THE TOP BAR -->
40 41
     <ImageView
41 42
         android:id="@+id/logo_persistent"
42 43
         android:layout_width="?attr/actionBarSize"
43 44
         android:layout_height="?attr/actionBarSize"
44
-        app:srcCompat="@drawable/actionbar_logo"
45
+        app:srcCompat="@drawable/logo_roundsquare"
45 46
         app:layout_constraintTop_toTopOf="parent"
46 47
         app:layout_constraintStart_toStartOf="parent"
47 48
         android:padding="12dp"
48
-        android:contentDescription="@string/r_a_dio_icon" />
49
+        android:contentDescription="@string/r_a_dio_icon"
50
+        android:visibility="gone"/>
49 51
 
50 52
     <androidx.appcompat.widget.Toolbar
51 53
         android:id="@+id/toolbar"
@@ -54,7 +56,7 @@
54 56
         android:background="?attr/colorPrimary"
55 57
         android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
56 58
         app:menu="@menu/toolbar_menu"
57
-        app:layout_constraintStart_toEndOf="@id/logo_persistent"
59
+        app:layout_constraintStart_toEndOf="@+id/logo_persistent"
58 60
         app:layout_constraintEnd_toEndOf="parent"
59 61
         app:layout_constraintTop_toTopOf="parent"
60 62
         android:layout_marginLeft="-4dp"

+ 16 - 6
app/src/main/res/layout/fragment_nowplaying.xml 파일 보기

@@ -40,7 +40,7 @@
40 40
 
41 41
             app:layout_constraintStart_toStartOf="@id/imageGuideline"
42 42
             app:layout_constraintTop_toTopOf="@id/seek_bar_volume"
43
-            app:srcCompat="@drawable/actionbar_logo"
43
+            app:srcCompat="@drawable/logo_roundsquare"
44 44
             tools:ignore="HardcodedText" />
45 45
 
46 46
         <androidx.constraintlayout.widget.Guideline
@@ -72,7 +72,8 @@
72 72
             app:layout_constraintHorizontal_bias="1.0"
73 73
             app:layout_constraintVertical_weight="3"
74 74
             app:layout_constraintStart_toStartOf="@id/streamerPicture"
75
-            app:layout_constraintTop_toBottomOf="@id/streamerPicture" />
75
+            app:layout_constraintTop_toBottomOf="@id/streamerPicture"
76
+            android:visibility="gone"/>
76 77
         <TextView
77 78
             android:id="@+id/listenersCount"
78 79
             android:layout_width="0dp"
@@ -87,7 +88,9 @@
87 88
             app:layout_constraintBottom_toTopOf="@id/topInfoGuideline"
88 89
             app:layout_constraintEnd_toEndOf="parent"
89 90
             app:layout_constraintStart_toStartOf="@id/streamerPicture"
90
-            app:layout_constraintTop_toBottomOf="@id/streamerName" />
91
+            app:layout_constraintTop_toBottomOf="@id/streamerName"
92
+            android:visibility="gone"
93
+            />
91 94
 
92 95
         <androidx.constraintlayout.widget.Guideline
93 96
             android:id="@+id/topInfoGuideline"
@@ -170,6 +173,7 @@
170 173
                 android:orientation="vertical"
171 174
                 android:layout_marginEnd="8dp"
172 175
                 android:layout_marginRight="8dp"
176
+                android:visibility="gone"
173 177
                 >
174 178
 
175 179
                 <TextView
@@ -287,6 +291,7 @@
287 291
 
288 292
         <!--    android:progressDrawable="@drawable/progress_bar_progress"     -->
289 293
 
294
+        <!-- REMOVE VISIBILITY GONE IF YOU HAVE TIME VALUES TO DISPLAY THE PROGRESS BAR -->
290 295
         <ProgressBar
291 296
             android:id="@+id/progressBar"
292 297
             style="@style/Widget.AppCompat.ProgressBar.Horizontal"
@@ -297,8 +302,10 @@
297 302
             android:progress="70"
298 303
             android:progressDrawable="@drawable/progress_bar_progress"
299 304
             app:layout_constraintBottom_toTopOf="@id/play_pause"
300
-            tools:layout_editor_absoluteX="0dp" />
305
+            tools:layout_editor_absoluteX="0dp"
306
+            android:visibility="gone"/>
301 307
 
308
+        <!-- REMOVE VISIBILITY GONE IF YOU HAVE TIME VALUES TO DISPLAY THE PROGRESS BAR -->
302 309
         <TextView
303 310
             android:id="@+id/endTime"
304 311
             android:layout_width="44sp"
@@ -308,8 +315,10 @@
308 315
             android:text=""
309 316
             android:textAlignment="textEnd"
310 317
             app:layout_constraintEnd_toEndOf="@id/progressBar"
311
-            app:layout_constraintTop_toBottomOf="@id/progressBar" />
318
+            app:layout_constraintTop_toBottomOf="@id/progressBar"
319
+            android:visibility="gone"/>
312 320
 
321
+        <!-- REMOVE VISIBILITY GONE IF YOU HAVE TIME VALUES TO DISPLAY THE PROGRESS BAR -->
313 322
         <TextView
314 323
             android:id="@+id/currentTime"
315 324
             android:layout_width="44sp"
@@ -319,7 +328,8 @@
319 328
             android:text=""
320 329
             android:textAlignment="textStart"
321 330
             app:layout_constraintStart_toStartOf="@id/progressBar"
322
-            app:layout_constraintTop_toBottomOf="@id/progressBar" />
331
+            app:layout_constraintTop_toBottomOf="@id/progressBar"
332
+            android:visibility="gone"/>
323 333
 
324 334
 
325 335
 

+ 0 - 4
app/src/main/res/menu/bottom_nav_menu.xml 파일 보기

@@ -16,9 +16,5 @@
16 16
         android:icon="@drawable/ic_newspaper"
17 17
         android:title="@string/title_news"/>
18 18
 
19
-    <item
20
-        android:id="@+id/navigation_chat"
21
-        android:icon="@drawable/ic_chat_processing"
22
-        android:title="@string/title_chat"/>
23 19
 
24 20
 </menu>

+ 2 - 1
app/src/main/res/menu/toolbar_menu.xml 파일 보기

@@ -14,11 +14,12 @@
14 14
         android:icon="@drawable/ic_alarm"
15 15
         app:showAsAction="ifRoom"/>
16 16
 
17
-
17
+    <!--
18 18
     <item
19 19
         android:id="@+id/action_refresh"
20 20
         android:title="@string/action_refresh"
21 21
         app:showAsAction="never"/>
22
+    -->
22 23
 
23 24
     <!--
24 25
     <item

+ 2 - 2
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 파일 보기

@@ -1,5 +1,5 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2 2
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
-    <background android:drawable="@drawable/ic_launcher_background"/>
4
-    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
3
+    <background android:drawable="@color/ic_launcher_background"/>
4
+    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
5 5
 </adaptive-icon>

+ 2 - 2
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 파일 보기

@@ -1,5 +1,5 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2 2
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
-    <background android:drawable="@drawable/ic_launcher_background"/>
4
-    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
3
+    <background android:drawable="@color/ic_launcher_background"/>
4
+    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
5 5
 </adaptive-icon>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png 파일 보기


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png 파일 보기


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png 파일 보기


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png 파일 보기


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png 파일 보기


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png 파일 보기


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png 파일 보기


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png 파일 보기


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 파일 보기


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png 파일 보기


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png 파일 보기


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 파일 보기


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 파일 보기


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png 파일 보기


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png 파일 보기


+ 0 - 13
app/src/main/res/navigation/navigation_chat.xml 파일 보기

@@ -1,13 +0,0 @@
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_chat"
6
-    app:startDestination="@id/fragment_chat"
7
-    tools:ignore="UnusedNavigation">
8
-    <fragment
9
-        android:id="@+id/fragment_chat"
10
-        android:name="fr.forum_thalie.tsumugi.ui.chat.ChatFragment"
11
-        android:label="@string/title_chat"
12
-        tools:layout="@layout/fragment_chat" />
13
-</navigation>

+ 4 - 0
app/src/main/res/values/ic_launcher_background.xml 파일 보기

@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+    <color name="ic_launcher_background">#F58B01</color>
4
+</resources>

+ 1 - 1
app/src/main/res/values/strings.xml 파일 보기

@@ -29,7 +29,7 @@
29 29
     <string name="action_settings">Settings</string>
30 30
     <string name="action_refresh">Force data refresh</string>
31 31
     <string name="action_bug_submit">Submit a bug (GitHub)</string>
32
-    <string name="github_url_new_issue">https://github.com/yattoz/Radio2/issues/</string>
32
+    <string name="github_url_new_issue">https://github.com/yattoz/Tsumugi-app/issues/</string>
33 33
     <string name="no_user_name">To display your favorite, set you IRC user name in Settings → Customize app behavior.</string>
34 34
     <string name="ra_f">.ra f</string>
35 35
 

+ 2 - 2
app/src/main/res/values/styles.xml 파일 보기

@@ -6,13 +6,13 @@
6 6
         <item name="colorPrimary">@color/colorPrimary</item>
7 7
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
8 8
         <item name="colorAccent">@color/colorAccent</item>
9
-        <item name="icon">@drawable/actionbar_logo</item>
9
+        <item name="icon">@drawable/logo_roundsquare</item>
10 10
         <item name="android:textColorHighlight">@color/rblue</item>
11 11
         <item name="android:colorBackground">@color/colorPrimaryDark</item>
12 12
     </style>
13 13
 
14 14
     <style name="AppTheme.Parameters" parent="AppTheme">
15
-        <item name="icon" />
15
+        <item name="icon"/>
16 16
     </style>
17 17
 
18 18
     <style name="AppTheme.BottomBar" parent="AppTheme">

+ 1 - 26
app/src/main/res/xml/customize_preferences.xml 파일 보기

@@ -2,14 +2,6 @@
2 2
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
3 3
     xmlns:app="http://schemas.android.com/apk/res-auto">
4 4
 
5
-
6
-
7
-    <EditTextPreference
8
-        app:iconSpaceReserved="false"
9
-        app:key="userName"
10
-        app:title="User name for favorite songs"
11
-        />
12
-
13 5
     <SeekBarPreference
14 6
         app:key="volume"
15 7
         app:iconSpaceReserved="false"
@@ -34,24 +26,7 @@
34 26
         app:iconSpaceReserved="false"
35 27
         app:title="Make Snackbars persistent"
36 28
         app:singleLineTitle="false"
37
-        app:defaultValue="true"
38
-        />
39
-
40
-    <ListPreference
41
-        app:key="fetchPeriod"
42
-        app:iconSpaceReserved="false"
43
-        android:title="Set update period when stopped"
44
-        app:singleLineTitle="false"
45
-        android:entries="@array/fetchPeriodString"
46
-        android:entryValues="@array/fetchPeriodValues"
47
-        android:defaultValue="10"
48
-        />
49
-
50
-    <Preference
51
-        app:key="helpFavorites"
52
-        app:title="Help to use favorites with IRC"
53
-        app:summary="Opens the wiki in your web browser"
54
-        app:icon="@drawable/ic_open_in_browser"
29
+        app:defaultValue="false"
55 30
         />
56 31
 
57 32
 </PreferenceScreen>

+ 0 - 7
app/src/main/res/xml/preferences.xml 파일 보기

@@ -25,13 +25,6 @@
25 25
         app:fragment="fr.forum_thalie.tsumugi.preferences.CustomizeFragment"
26 26
         />
27 27
 
28
-    <Preference
29
-        app:icon="@drawable/ic_notification"
30
-        app:key="streamerNotifServiceFragment"
31
-        app:title="Streamer Notification Service"
32
-        app:fragment="fr.forum_thalie.tsumugi.preferences.StreamerNotifServiceFragment"
33
-        />
34
-
35 28
     <Preference
36 29
         app:key="submitBug"
37 30
         app:title="Submit a bug (opens GitHub in a web browser)"

+ 0 - 22
app/src/main/res/xml/streamer_notif_service_preferences.xml 파일 보기

@@ -1,22 +0,0 @@
1
-<?xml version="1.0" encoding="utf-8"?>
2
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
3
-    xmlns:app="http://schemas.android.com/apk/res-auto">
4
-
5
-        <SwitchPreferenceCompat
6
-            app:key="newStreamerNotification"
7
-            app:iconSpaceReserved="false"
8
-            app:title="New streamer notification (drains battery!)"
9
-            app:singleLineTitle="false"
10
-            app:defaultValue="false"
11
-            />
12
-
13
-        <ListPreference
14
-            app:iconSpaceReserved="false"
15
-            app:key="streamerMonitorPeriodPref"
16
-            app:title="Streamer monitor frequency"
17
-            android:entries="@array/streamerFetchPeriodString"
18
-            android:entryValues="@array/streamerFetchPeriod"
19
-            android:defaultValue="10"
20
-            />
21
-
22
-</PreferenceScreen>