Skip to content
This repository has been archived by the owner on Jan 31, 2024. It is now read-only.

Offline mode implementation #277

Merged
merged 8 commits into from
May 12, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ch.sdp.vibester.model

import androidx.test.platform.app.InstrumentationRegistry
import ch.sdp.vibester.api.LastfmMethod
import org.junit.Assert
import org.junit.Test

class OfflineSongListTest {

@Test
fun noSongsAvailableInitialization() {
val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
val mySongsList = OfflineSongList(context, LastfmMethod.BY_TAG.method)

val inputSongsList = mutableListOf<Pair<String, String>>()
val page = "1"
val songsPerPage = "100"
val totalPages = "20"
val totalSongs = "2000"


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space

Assert.assertEquals(inputSongsList, mySongsList.getSongList())
Assert.assertEquals(page, mySongsList.getPage())
Assert.assertEquals(songsPerPage, mySongsList.getSongsPerPage())
Assert.assertEquals(totalPages, mySongsList.getTotalPages())
Assert.assertEquals(totalSongs, mySongsList.getTotalSongs())
Assert.assertTrue(mySongsList.getEmptySongs())
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/ch/sdp/vibester/activity/DownloadActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,30 @@ class DownloadActivity : AppCompatActivity() {
records.createNewFile()
}
records.appendText("$songName\n")
/* TODO: OFFLINE
recordProperties()
*/
}

/**
* Records the properties of a song.
* Order of storage: Track name - artist name - artwork URL - preview URL.
*/
/* TODO: OFFLINE
private fun recordProperties() {
var properties = File(applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "properties.txt")

if(!properties.exists()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after if

properties.createNewFile()
}

val trackName = song.getTrackName()
val artistName = song.getArtistName()
val artworkURL = song.getArtworkUrl()
val previewURL = song.getPreviewUrl()
properties.appendText("$trackName - $artistName - $artworkURL - $previewURL\n")
}
*/

fun switchToDeleteSongs(view: View) {
val intent = Intent(this, DeleteSongsActivity::class.java)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/ch/sdp/vibester/api/AudioPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class AudioPlayer private constructor() {
companion object {
/**
* A function that given an audio stream url will play it
* @param audioUrl Url in String pointing towards the audio stream
* @param audioUrl Url in String pointing towards the audio stream OR the path to the file to be listened to
* @return CompletableFuture<MediaPlayer> that contains the current playing mediaPlayer
*/
fun playAudio(audioUrl: String): CompletableFuture<MediaPlayer> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class GameSetupFragment : Fragment(), View.OnClickListener, AdapterView.OnItemSe
var game = "local_buzzer"
var gameSize = R.string.one.toString()
lateinit var gameManager: GameManager
/* TODO: OFFLINE
private var hasInternet: Boolean = true
*/

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
Expand Down Expand Up @@ -159,6 +162,10 @@ class GameSetupFragment : Fragment(), View.OnClickListener, AdapterView.OnItemSe
call.enqueue(object : Callback<Any> {
override fun onFailure(call: Call<Any>, t: Throwable?) {}
override fun onResponse(call: Call<Any>, response: Response<Any>) {
/* TODO: OFFLINE
gameManager.setInternet()
gameManager.setContext(context)
*/
gameManager.setGameSongList(Gson().toJson(response.body()), uri.method)
}
})
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/java/ch/sdp/vibester/helper/BuzzerGameManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class BuzzerGameManager: GameManager() {
lateinit var score: BuzzerScoreUpdater
private lateinit var mediaPlayer: CompletableFuture<MediaPlayer>
lateinit var scoreUpdater: BuzzerScoreUpdater
private var hasInternet: Boolean = true

/**
* Check if mediaPlayer is initialized
Expand All @@ -22,7 +23,15 @@ class BuzzerGameManager: GameManager() {
* Play current song with media player.
*/
fun playSong() {
mediaPlayer = AudioPlayer.playAudio(currentSong.getPreviewUrl())
if(hasInternet) {
mediaPlayer = AudioPlayer.playAudio(currentSong.getPreviewUrl())
} else {
mediaPlayer = AudioPlayer.playAudio(currentSong.getPreviewUrl())
/* TODO: OFFLINE
var media = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "extract_of_$currentSong.getTrackName()")
mediaPlayer = AudioPlayer.playAudio(media.absolutePath)
*/
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you just put these modifications into GameManager instead? I deleted BuzzerGameManager when refactoring and put all the equivalent functions into GameManager instead so you should find it there

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done on local, will push soon once I make sure the merge was successful

}

/**
Expand Down
129 changes: 129 additions & 0 deletions app/src/main/java/ch/sdp/vibester/model/OfflineSongList.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ch.sdp.vibester.model

import android.content.Context
import android.os.Environment
import ch.sdp.vibester.api.LastfmMethod
import java.io.BufferedReader
import java.io.File
import java.io.FileReader

/**
* Process the fetched data from the external storage of the app.
* Mainly, it creates a list of songs in the form Pair("$songName", "$artistName")
* @param ctx: Context of the caller, to fetch the downloaded folder
* @param method: Chosen playlist type
*/
class OfflineSongList(ctx: Context, method: String) {
private var songList = mutableListOf<Pair<String, String>>()
private var page = ""
private var songsPerPage = ""
private var totalPages = ""
private var totalSongs = ""
private var context = ctx
private var emptySongs: Boolean = false

init {
try {
var tracksField = "tracks"
if (method == LastfmMethod.BY_ARTIST.method) {
tracksField = "toptracks"
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra spaces

fillList()

page = "1"
songsPerPage = "100"
totalPages = "20"
totalSongs = "2000"
Copy link
Collaborator

@zwierski zwierski May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These vars don't seem to be modified anywhere in the class: maybe it'd be more readable to turn them into private vals / constants that you directly initialise as they're declared.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, will do it


} catch (e: Exception) {
throw IllegalArgumentException("OfflineSongList constructor, failed reading from file or empty file!")
}
}

/**
* Adds from the downloads to the list of songs ("$songName", "$artistName")
* Saves the list of songs in songList
*/
private fun fillList() {
var records = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "records.txt")


if(!records.exists() || records.length() == 0L) {
Copy link
Collaborator

@zwierski zwierski May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after if for readability if (!records.exists() || records.length() == 0L)

Copy link
Collaborator Author

@Tsathogguaa Tsathogguaa May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done on local

//There are no downloaded songs, keep the song list empty
emptySongs = true
} else {
var reader = BufferedReader(FileReader(records))
var currentLine = reader.readLine()

while(currentLine != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after while

var trimmed = currentLine.trim()
if(trimmed.isNotEmpty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after if

val split = trimmed.split("-")
if(split.size == 2) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after if

songList.add(Pair(split[0], split[1]))
}
}
currentLine = reader.readLine()
}
reader.close()
emptySongs = false
}
}

/**
* Getter that return songs for the given tag
* @return MutableList<Pair<String,String>> of type Pair("$songName", "$artistName")
*/
fun getSongList(): MutableList<Pair<String, String>> {
return songList
}

/**
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

* Getter that return shuffled song list
* @return MutableList<Pair<String,String>> of type Pair("$songName", "$artistName")
*/
fun getShuffledSongList(): MutableList<Pair<String, String>> {
return songList.asSequence().shuffled().toMutableList()
}

/**
* Getter that return page number from the query
* @return String page
*/
fun getPage(): String {
return page
}

/**
* Getter that return total number of songs in the tag
* @return String totalSongs
*/
fun getTotalSongs(): String {
return totalSongs
}

/**
* Getter that return the total number of pages with songs by tag
* @return String totalPages
*/
fun getTotalPages(): String {
return totalPages
}

/**
* Getter that return the total number of songs in the page
* @return String perPage
*/
fun getSongsPerPage(): String {
return songsPerPage
}

/**
* Getter that returns whether there are downloaded songs available or not
* @return Boolean emptySongs
*/
fun getEmptySongs(): Boolean {
return emptySongs
}
}
1 change: 0 additions & 1 deletion app/src/main/java/ch/sdp/vibester/model/SongList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.json.JSONObject
* @param jsonMeta: Lastfm fetched data
*/
class SongList(jsonMeta: String, method: String) {
private var GAME_SIZE = 100
private var songList = mutableListOf<Pair<String, String>>()
private var page = ""
private var songsPerPage = ""
Expand Down
17 changes: 13 additions & 4 deletions app/src/main/res/layout/activity_download.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@
android:id="@+id/download_songName"
android:layout_width="253dp"
android:layout_height="50dp"
android:layout_marginTop="100dp"
android:layout_marginBottom="50dp"
android:layout_marginTop="39dp"
android:ems="10"
android:hint="@string/download_songname"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/download_downloadsong"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@+id/download_indicator"
tools:ignore="TextContrastCheck" />

<Button
Expand All @@ -57,4 +55,15 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/download_downloadsong" />

<TextView
android:id="@+id/download_indicator"
android:layout_width="313dp"
android:layout_height="59dp"
android:layout_marginTop="28dp"
android:text="@string/download_indicator"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
<string name="download_permission_denied">Permission denied, cannot download!</string>
<string name="download_already_downloading">A song is being downloaded, please be patient!</string>
<string name="download_please_retry_later">Please retry later!</string>
<string name="download_indicator">Please write the name of the song you desire to download in the following format:\n Song Name - Artist Name</string>

<string name="Singleplayer">Single-player</string>
<string name="Multiplayer">Multiplayer</string>
Expand Down