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

Jwen/show lyric #84

Merged
merged 12 commits into from
Mar 16, 2022
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.firebase:firebase-auth-ktx:21.0.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20180813'
testImplementation 'org.robolectric:robolectric:4.6'
Expand Down
70 changes: 70 additions & 0 deletions app/src/androidTest/java/ch/sdp/vibester/LyricTemporaryTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ch.sdp.vibester

import android.content.Intent
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import ch.sdp.vibester.api.LyricsOVHApiInterface
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Test

class LyricTemporaryTest {
@Test
fun canGetLyricsFromAPI() {
val service = LyricsOVHApiInterface.create("https://api.lyrics.ovh/")
val lyric = service.getLyrics("Imagine Dragons", "Believer").execute()
assertThat(lyric.body().lyrics, equalTo("First things first\r\n" +
"I'm say all the words inside my head\r\n" +
"I'm fired up and tired of the way that things have been, oh-ooh\r\n" +
"The way that things have been, oh-ooh\r\nSecond thing second\r\n" +
"Don't you tell me what you think that I can be\n\n" +
"I'm the one at the sail, I'm the master of my sea, oh-ooh\n\n" +
"The master of my sea, oh-ooh\n\n\n\nI was broken from a young age\n\n" +
"Taking my sulkin to the masses\n\nWrite down my poems for the few\n\n" +
"That looked at me took to me, shook to me, feeling me\n\n" +
"Singing from heart ache from the pain\n\nTake up my message from the veins\n\n" +
"Speaking my lesson from the brain\n\nSeeing the beauty through the...\n\n\n\n" +
"Pain!\n\nYou made me a, you made me a believer, believer\n\nPain!" +
"\n\nYou break me down, you build me up, believer, believer\n\nPain!\n\n" +
"I let the bullets fly, oh let them rain\n\nMy life, my love, my drive, it came from...\n\n" +
"Pain!\n\nYou made me a, you made me a believer, believer\n\n\n\n" +
"Third things third\n\nSend a prayer to the ones up above\n\n" +
"All the hate that you've heard has turned your spirit to a dove, oh-ooh\n\n" +
"Your spirit up above, oh-ooh\n\n\n\nI was choking in the crowd\n\nLiving my brain up in the cloud\n\n" +
"Falling like ashes to the ground\n\nHoping my feelings, they would drown\n\n" +
"But they never did, ever lived, ebbing and flowing\n\nInhibited, limited\n\n" +
"Till it broke up and it rained down\n\nIt rained down, like...\n\n\n\nPain!\n\n" +
"You made me a, you made me a believer, believer\n\nPain!\n\n" +
"You break me down, you build me up, believer, believer\n\nPain!\n\n" +
"I let the bullets fly, oh let them rain\n\nMy life, my love, my drive, they came from...\n\n" +
"Pain!\n\nYou made me a, you made me a believer, believer\n\n\n\nLast things last\n\n" +
"By the grace of the fire and the flames\n\nYou're the face of the future, you're the blood in my veins, oh-ooh\n\n" +
"The blood in my veins, oh-ooh\n\nBut they never did, ever lived, ebbing and flowing\n\nInhibited, limited\n\n" +
"Till it broke up and it rained down\n\nIt rained down, like...\n\n\n\nPain!" +
"\n\nYou made me a, you made me a believer, believer\n\nPain!\n\n" +
"You break me down, you build me up, believer, believer\n\nPain!\n\n" +
"I let the bullets fly, oh let them rain\n\nMy life, my love, my drive, they came from...\n\n" +
"Pain!\n\nYou made me a, you made me a believer, believer"))
}

@Test
fun lyricTemporaryTest() {
val inputArtistName = "Imagine Dragons"
val inputTrackName = "Believer"
val intent = Intent(
ApplicationProvider.getApplicationContext(), LyricTemporary::class.java
)
val scn: ActivityScenario<GreetingActivity> = ActivityScenario.launch(intent)
onView(withId(R.id.artistForLyric)).perform(typeText(inputArtistName), closeSoftKeyboard())
onView(withId(R.id.trackForLyric)).perform(typeText(inputTrackName), closeSoftKeyboard())
onView(withId(R.id.validateForLyric)).perform(click())
Thread.sleep(1500) // wait for API response
onView(withId(R.id.lyricBody)).check(matches(withText(containsString("First things first"))))
}
}
18 changes: 6 additions & 12 deletions app/src/androidTest/java/ch/sdp/vibester/MainActivityTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,15 @@ class MainActivityTest {
intended(hasExtra("name", inputName))
}

@Test
fun scoreboardTest() {
onView(withId(R.id.scoreboardButton)).perform(click())
intended(hasComponent(ScoreBoardActivity::class.qualifiedName))
}

@Test
fun profileTest() {
onView(withId(R.id.profileButton)).perform(click())
intended(hasComponent(ProfileSetup::class.qualifiedName))
}

@Test
fun checkIntentOnWelcome(){ //FILLER TESTING
onView(withId(R.id.placeholder_welcome)).perform(click())
intended(hasComponent(WelcomeScreen::class.qualifiedName))
}

@Test
fun checkIntentOnLyric() {
onView(withId(R.id.lyricButton)).perform(click())
intended(hasComponent(LyricTemporary::class.qualifiedName))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ package ch.sdp.vibester
import android.content.Intent
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
Expand All @@ -28,7 +24,7 @@ class MusicTemporaryTest{
val scn: ActivityScenario<GreetingActivity> = ActivityScenario.launch(intent)
onView(withId(R.id.musicName))
.perform(typeText(inputName), closeSoftKeyboard())
onView(withId(R.id.validate)).perform(click())
onView(withId(R.id.validateForMusic)).perform(click())
onView(withId(R.id.textViewPlaying))
.check(matches(withText("Imagine Dragons - Believer")))
}
Expand Down
105 changes: 55 additions & 50 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,51 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ch.sdp.vibester">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name="ch.sdp.vibester.GamescreenActivity"
android:exported="false" />
<activity
android:name=".profile.ProfileSetup"
android:exported="false" />
<activity
android:name=".MusicTemporary"
android:exported="false" />
<activity
android:name=".GameSetupScreen"
android:exported="false" />
<activity android:name=".scoreboard.ScoreBoardActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".WelcomeScreen"
android:exported="false" />
<activity
android:name=".Register"
android:exported="true" />
<activity
android:name=".GreetingActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ch.sdp.vibester">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name="ch.sdp.vibester.GamescreenActivity"
android:exported="false" />
<activity
android:name=".profile.ProfileSetup"
android:exported="false" />
<activity
android:name=".MusicTemporary"
android:exported="false" />
<activity
android:name=".LyricTemporary"
android:exported="false" />
<activity
android:name=".GameSetupScreen"
android:exported="false" />
<activity
android:name=".scoreboard.ScoreBoardActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".WelcomeScreen"
android:exported="false" />
<activity
android:name=".Register"
android:exported="true" />
<activity
android:name=".GreetingActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
47 changes: 47 additions & 0 deletions app/src/main/java/ch/sdp/vibester/LyricTemporary.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ch.sdp.vibester

import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import ch.sdp.vibester.api.LyricsOVHApiInterface
import ch.sdp.vibester.model.Lyric
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


class LyricTemporary: AppCompatActivity() {
private val baseUrl = "https://api.lyrics.ovh/"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lyric_temporary)

val artistName = findViewById<EditText>(R.id.artistForLyric)

val trackName = findViewById<EditText>(R.id.trackForLyric)

val btnValidate = findViewById<Button>(R.id.validateForLyric)

val textViewLyric = findViewById<TextView>(R.id.lyricBody)
textViewLyric.movementMethod = ScrollingMovementMethod()

btnValidate.setOnClickListener {
val service = LyricsOVHApiInterface.create(baseUrl)

val call = service.getLyrics(artistName.text.toString(), trackName.text.toString())
call.enqueue(object: Callback<Lyric>{
override fun onFailure(call: Call<Lyric>?, t: Throwable?) {}

override fun onResponse(call: Call<Lyric>?, response: Response<Lyric>?) {
if (response != null) {
textViewLyric.text = response.body().lyrics
}
}
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think the logic of fetching data should be left in activity or moved somewhere else?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree that way will be more consistent with the existing code we have, but from all the examples I have seen using retrofit they all put it like this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was also fetching data. I did not use retrofit, I used okhttp3 and android.net.uri to build uri. I followed Maxime's logic in ItunesMusicApi.kt. He has created a class for fetching data with callbacks in it. So, when there was a click on the button, he just called this class to fetch data. I am not saying it is correct, but I liked that there is some separation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We can keep the way you did!

}
}
}
24 changes: 4 additions & 20 deletions app/src/main/java/ch/sdp/vibester/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,15 @@ class MainActivity : AppCompatActivity() {
val btnGreeting = findViewById<Button>(R.id.mainButton)

val greetingIntent = Intent(this, GreetingActivity::class.java)

btnGreeting.setOnClickListener {
greetingIntent.putExtra("name", txtInput.text.toString())
startActivity(greetingIntent)
}


val btnProfile = findViewById<Button>(R.id.profileButton)
val profileIntent = Intent(this, ProfileSetup::class.java)

btnProfile.setOnClickListener{
val userID = (0..5).random().toString()
val dataProvider = userID.let { ProfileDataProvider(it) }
val user: UserProfile = dataProvider.getUserProfileData()
profileIntent.putExtra("userProfile", user)
startActivity(profileIntent)
}

// button to scoreboard
// FIXME: scoreboard enter button need to be move to the welcome screen
val btnScoreboard = findViewById<Button>(R.id.scoreboardButton)
val scoreboardIntent = Intent(this, ScoreBoardActivity::class.java)

btnScoreboard.setOnClickListener {
startActivity(scoreboardIntent)
val btnLyric = findViewById<Button>(R.id.lyricButton)
val lyricIntent = Intent(this, LyricTemporary::class.java)
btnLyric.setOnClickListener {
startActivity(lyricIntent)
}

}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/ch/sdp/vibester/MusicTemporary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ class MusicTemporary : AppCompatActivity() {

val txtInput = findViewById<EditText>(R.id.musicName)

val btnValidate = findViewById<Button>(R.id.validate)
val btnValidate = findViewById<Button>(R.id.validateForMusic)

val textViewPlaying = findViewById<TextView>(R.id.textViewPlaying)

btnValidate.setOnClickListener {
val song = Song(ItunesMusicApi.querySong(txtInput.text.toString(), OkHttpClient()).get())
ItunesMusicApi.playAudio(song.getPreviewUrl())
textViewPlaying.setText(song.getArtistName() + " - " + song.getTrackName())
textViewPlaying.text = song.getArtistName() + " - " + song.getTrackName()
}


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

import ch.sdp.vibester.model.Lyric
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path

interface LyricsOVHApiInterface {
jiabaow marked this conversation as resolved.
Show resolved Hide resolved
/**
* Given the name of the artist and the title of the track
* return a Call<Lyric> contains the lyric of the track
*/
@GET("v1/{artist}/{title}")
fun getLyrics(@Path("artist") artist: String, @Path("title") title: String): Call<Lyric>

companion object {
/**
* @param baseUrl base url of the api
* return an instance of the api interface
*/
fun create(baseUrl:String): LyricsOVHApiInterface {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
return retrofit.create(LyricsOVHApiInterface::class.java)
}
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/ch/sdp/vibester/model/Lyric.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ch.sdp.vibester.model

import com.google.gson.annotations.SerializedName


class Lyric {
@SerializedName("lyrics")
var lyrics: String? = null

//not used
//@SerializedName("error")
//var error: String? = null
}
2 changes: 1 addition & 1 deletion app/src/main/java/ch/sdp/vibester/model/Song.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Song(jsonMeta: String) {
artworkUrl = jsonRes.getString("artworkUrl100")
trackName = jsonRes.getString("trackName")
artistName = jsonRes.getString("artistName")
} catch(e: Exception){
} catch(e: Exception) {
throw IllegalArgumentException("Song constructor, bad argument")
}
}
Expand Down
Loading