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,57 @@
package ch.sdp.vibester.model

import android.os.Environment
import androidx.test.platform.app.InstrumentationRegistry
import ch.sdp.vibester.api.LastfmMethod
import org.junit.Assert
import org.junit.Test
import java.io.File

class OfflineSongListTest {

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

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

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())
}

@Test
fun addSpecificSongToList() {
val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
var records = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "records.txt")
records.createNewFile()
records.appendText("bones - imagine dragons\n")

val mySongsList = OfflineSongList(context)

val songName = "bones"
val artistName = "imagine dragons"
val inputSongsList = mutableListOf(Pair(songName, artistName))
val page = "1"
val songsPerPage = "100"
val totalPages = "20"
val totalSongs = "2000"

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())

records.delete()
}
}
34 changes: 28 additions & 6 deletions app/src/main/java/ch/sdp/vibester/activity/DownloadActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class DownloadActivity : AppCompatActivity() {
* Function that handles deletion button pushes.
*/
private fun downloadListener(songView: TextView) {
if(downloadStarted) {
if (downloadStarted) {
Toast.makeText(applicationContext, getString(R.string.download_already_downloading), Toast.LENGTH_LONG).show()
editTextView(getString(R.string.download_please_retry_later), songView)
} else {
Expand Down Expand Up @@ -138,8 +138,8 @@ class DownloadActivity : AppCompatActivity() {

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == STORAGE_PERMISSION_CODE) {
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadId = startDownload()
}
} else {
Expand All @@ -151,9 +151,9 @@ class DownloadActivity : AppCompatActivity() {
* Checks if the required app permissions are already given. If not, request those permissions.
*/
private fun checkPermissionsAndDownload() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
} else {
downloadId = startDownload()
Expand Down Expand Up @@ -200,12 +200,34 @@ class DownloadActivity : AppCompatActivity() {
private fun record() {
var records = File(applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "records.txt")

if(!records.exists()) {
if (!records.exists()) {
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()) {
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 @@ -35,6 +35,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 @@ -157,6 +160,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/GameManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ open class GameManager : Serializable {
private var artistName = ""
private var songName = ""
var nextSongInd = 0
private var hasInternet: Boolean = true

private lateinit var mediaPlayer: CompletableFuture<MediaPlayer>

Expand Down Expand Up @@ -161,7 +162,15 @@ open class GameManager : Serializable {
* 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)
*/
}
}

/**
Expand Down
114 changes: 114 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,114 @@
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) {
private var songList = mutableListOf<Pair<String, String>>()
private val page = "1"
private val songsPerPage = "100"
private val totalPages = "20"
private val totalSongs = "2000"
private var context = ctx
private var emptySongs: Boolean = false

init {
fillList()
}

/**
* 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) {
//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) {
var trimmed = currentLine.trim()
if (trimmed.isNotEmpty()) {
val split = trimmed.split("-")
if (split.size == 2) {
songList.add(Pair(split[0].trim(), split[1].trim()))
}
}
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 getShuffledDownloadedSongList(): 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 @@ -114,6 +114,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