diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 6da18af..38349af 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,14 +28,23 @@ androidExtensions { experimental = true } +kotlin { + experimental { + coroutines "enable" + } +} + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.25.3" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:support-v4:28.0.0' testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.22.0' + androidTestImplementation 'com.android.support.test:rules:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' @@ -45,6 +54,10 @@ dependencies { //anko implementation "org.jetbrains.anko:anko:$anko_version" implementation "org.jetbrains.anko:anko-design:$anko_version" + implementation "org.jetbrains.anko:anko-coroutines:$anko_version" + implementation "org.jetbrains.anko:anko-sdk15-coroutines:$anko_version" + implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version" + implementation "org.jetbrains.anko:anko-design-coroutines:$anko_version" // RecyclerView-v7 implementation "org.jetbrains.anko:anko-recyclerview-v7:$anko_version" @@ -52,4 +65,15 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.google.code.gson:gson:2.8.5' + + //mockito + testImplementation 'org.mockito:mockito-core:2.22.0' + testImplementation "org.mockito:mockito-inline:2.22.0" + + androidTestImplementation("com.android.support.test.espresso:espresso-contrib:2.2.2") { + exclude group: 'com.android.support', module: 'appcompat-v7' + exclude group: 'com.android.support', module: 'support-v4' + exclude group: 'com.android.support', module: 'design' + exclude group: 'com.android.support', module: 'recyclerview-v7' + } } diff --git a/app/src/androidTest/java/com/example/wijaya_pc/eplmatchschedule/MainActivityTest.kt b/app/src/androidTest/java/com/example/wijaya_pc/eplmatchschedule/MainActivityTest.kt new file mode 100644 index 0000000..8d23a9e --- /dev/null +++ b/app/src/androidTest/java/com/example/wijaya_pc/eplmatchschedule/MainActivityTest.kt @@ -0,0 +1,198 @@ +package com.example.wijaya_pc.eplmatchschedule + +import android.support.test.espresso.Espresso +import android.support.test.espresso.Espresso.onView +import android.support.test.espresso.Espresso.pressBack +import android.support.test.espresso.action.ViewActions.click +import android.support.test.espresso.assertion.ViewAssertions.matches +import android.support.test.espresso.contrib.RecyclerViewActions +import android.support.test.espresso.matcher.ViewMatchers +import android.support.test.espresso.matcher.ViewMatchers.isDisplayed +import android.support.test.espresso.matcher.ViewMatchers.withId +import android.support.test.rule.ActivityTestRule +import android.support.test.runner.AndroidJUnit4 +import android.support.v7.widget.RecyclerView +import com.example.wijaya_pc.eplmatchschedule.R.id.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MainActivityTest { + @Rule + @JvmField var activityRule = ActivityTestRule(MainActivity::class.java) + + @Test + fun testRecyclerViewLastMatchBehaviour() { + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(14)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(1)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.actionOnItemAtPosition(1, click())) + } + + @Test + fun testRecyclerViewNextMatchBehaviour() { + Thread.sleep(1000) + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(tab_next_match)).perform(click()) + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(7)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(3)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.actionOnItemAtPosition(3, click())) + } + + @Test + fun testRecyclerViewFavoritesBehaviour() { + Thread.sleep(1000) + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(favorites)).perform(click()) + Thread.sleep(1000) + + onView(withId(listFav)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(listFav)).perform(RecyclerViewActions.scrollToPosition(14)) + Thread.sleep(1000) + onView(withId(listFav)).perform(RecyclerViewActions.scrollToPosition(0)) + Thread.sleep(1000) + onView(withId(listFav)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click())) + } + + + @Test + fun testAddFavoriteFromLastMatchBehaviour() { + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + onView(ViewMatchers.withText("Crystal Palace")).perform(click()) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + onView(withId(add_to_favorite)).perform(click()) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(add_to_favorite)).perform(click()) + Thread.sleep(1000) + onView(ViewMatchers.withText("Removed from FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(add_to_favorite)).perform(click()) + Thread.sleep(1000) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + pressBack() + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(5)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.actionOnItemAtPosition(5, click())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + onView(withId(add_to_favorite)).perform(click()) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + + pressBack() + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(favorites)).perform(click()) + Thread.sleep(1000) + } + + @Test + fun testAddFavoriteFromNextMatchBehaviour() { + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(tab_next_match)).perform(click()) + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + onView(ViewMatchers.withText("Liverpool")).perform(click()) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + onView(withId(add_to_favorite)).perform(click()) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(add_to_favorite)).perform(click()) + Thread.sleep(1000) + onView(ViewMatchers.withText("Removed from FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(add_to_favorite)).perform(click()) + Thread.sleep(1000) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + + pressBack() + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(listMatch)).perform(RecyclerViewActions.scrollToPosition(8)) + Thread.sleep(1000) + onView(withId(listMatch)).perform(RecyclerViewActions.actionOnItemAtPosition(8, click())) + Thread.sleep(1000) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + onView(withId(add_to_favorite)).perform(click()) + onView(ViewMatchers.withText("Added to FavoriteMatches")).check(matches(isDisplayed())) + + pressBack() + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(favorites)).perform(click()) + Thread.sleep(1000) + } + + @Test + fun testDeleteFavoriteFromFavoritesFragmentBehaviour() { + Thread.sleep(1000) + + onView(withId(listMatch)).check(matches(isDisplayed())) + Thread.sleep(1000) + + onView(withId(bottom_navigation)).check(matches(isDisplayed())) + onView(withId(favorites)).perform(click()) + Thread.sleep(1000) + + onView(withId(listFav)).check(matches(isDisplayed())) + Thread.sleep(1000) + onView(withId(listFav)).perform(RecyclerViewActions.scrollToPosition(0)) + Thread.sleep(1000) + onView(withId(listFav)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click())) + + onView(withId(add_to_favorite)).check(matches(isDisplayed())) + onView(withId(add_to_favorite)).perform(click()) + onView(ViewMatchers.withText("Removed from FavoriteMatches")).check(matches(isDisplayed())) + Thread.sleep(1000) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/DetailActivity.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/DetailActivity.kt index cacff09..69e189c 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/DetailActivity.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/DetailActivity.kt @@ -1,6 +1,5 @@ package com.example.wijaya_pc.eplmatchschedule -import android.database.sqlite.SQLiteConstraintException import android.os.Bundle import android.support.v4.content.ContextCompat import android.support.v7.app.AppCompatActivity @@ -24,18 +23,21 @@ import com.example.wijaya_pc.eplmatchschedule.ui.DetailMatchUI import com.example.wijaya_pc.eplmatchschedule.view.DetailView import com.google.gson.Gson import com.squareup.picasso.Picasso +import org.jetbrains.anko.ctx import org.jetbrains.anko.db.classParser -import org.jetbrains.anko.db.delete -import org.jetbrains.anko.db.insert import org.jetbrains.anko.db.select import org.jetbrains.anko.design.snackbar import org.jetbrains.anko.find import org.jetbrains.anko.setContentView -import java.text.SimpleDateFormat class DetailActivity : AppCompatActivity(), DetailView { + /*companion object { + const val dataParcel = "data_parcel" + }*/ + + private lateinit var detailView: ScrollView private lateinit var progressBar: ProgressBar @@ -93,7 +95,24 @@ class DetailActivity : AppCompatActivity(), DetailView { true } add_to_favorite -> { - if (isFavorite) removeFromFavorite() else addToFavorite() + // if (isFavorite) removeFromFavorite() else addToFavorite() + + if (isFavorite) { + detailPresenter.removeFromFavorite(ctx, matches.matchId) + snackbar(detailView, "Removed from FavoriteMatches").show() + } else { + detailPresenter.addToFavorite( + ctx, matches.matchId, + dateToSimpleString(matches.matchDate), + matches.idHomeTeam, + matches.idAwayTeam, + matches.homeTeam, + matches.awayTeam, + matches.homeScore, + matches.awayScore + ) + snackbar(detailView, "Added to FavoriteMatches").show() + } isFavorite = !isFavorite setFavorite() @@ -105,6 +124,7 @@ class DetailActivity : AppCompatActivity(), DetailView { } + override fun getMatch(data: Match) { matches = Match( data.matchId, @@ -114,11 +134,27 @@ class DetailActivity : AppCompatActivity(), DetailView { data.homeTeam, data.awayTeam, data.homeScore, - data.awayScore + data.awayScore, + data.homeFormation, + data.awayFormation, + data.homeGoals, + data.awayGoals, + data.homeShots, + data.awayShots, + data.homeGoalKeeper, + data.awayGoalKeeper, + data.homeDefence, + data.awayDefence, + data.homeMidfield, + data.awayMidfield, + data.homeForward, + data.awayForward, + data.homeSubstitutes, + data.awaySubtitutes ) val matchDate: TextView = find(match_date) - matchDate.text = (SimpleDateFormat("EEE, dd MMM yyyy").format(data.matchDate)).toString() + matchDate.text = dateToSimpleString(data.matchDate) val hometeam: TextView = find(match_home_team) val awayteam: TextView = find(match_away_team) @@ -178,41 +214,8 @@ class DetailActivity : AppCompatActivity(), DetailView { Picasso.get().load(data.teamBadge).into(if (homeTeam == true) homelogo else awaylogo) } - override fun addToFavorite() { - try { - database.use { - insert( - FavoriteMatches.TABLE_FAVORITE_MATCH, - FavoriteMatches.MATCH_ID to matches.matchId, - FavoriteMatches.MATCH_DATE to dateFormat.format(matches.matchDate).toString(), - FavoriteMatches.HOME_TEAM_ID to matches.idHomeTeam, - FavoriteMatches.AWAY_TEAM_ID to matches.idAwayTeam, - FavoriteMatches.HOME_TEAM_NAME to matches.homeTeam, - FavoriteMatches.AWAY_TEAM_NAME to matches.awayTeam, - FavoriteMatches.HOME_TEAM_SCORE to matches.homeScore, - FavoriteMatches.AWAY_TEAM_SCORE to matches.awayScore - ) - } - snackbar(detailView, "Added to FavoriteMatches").show() - } catch (e: SQLiteConstraintException) { - snackbar(detailView, e.localizedMessage).show() - } - } - - //fungsi untuk menghapus favorite - override fun removeFromFavorite() { - try { - database.use { - delete(FavoriteMatches.TABLE_FAVORITE_MATCH, "(MATCH_ID = {id})", "id" to matchID) - } - snackbar(detailView, "Removed from FavoriteMatches").show() - } catch (e: SQLiteConstraintException) { - snackbar(detailView, e.localizedMessage).show() - } - } - // fungsi untuk menandai match yang favorite - override fun setFavorite() { + private fun setFavorite() { if (isFavorite) menuItem?.getItem(0)?.icon = ContextCompat.getDrawable(this, ic_added_to_favorites) else @@ -220,7 +223,7 @@ class DetailActivity : AppCompatActivity(), DetailView { } - override fun favoriteState() { + private fun favoriteState() { database.use { val result = select(FavoriteMatches.TABLE_FAVORITE_MATCH) .whereArgs( diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/FavoriteMatchesFragment.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/FavoriteMatchesFragment.kt index 1ddb1d7..05a84af 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/FavoriteMatchesFragment.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/FavoriteMatchesFragment.kt @@ -69,6 +69,7 @@ class FavoriteMatchesFragment : Fragment(), AnkoComponent { ) listFav = recyclerView { + id = R.id.listFav lparams(width = matchParent, height = wrapContent) layoutManager = LinearLayoutManager(ctx) } diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MainActivity.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MainActivity.kt index 307d19d..83e7f81 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MainActivity.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MainActivity.kt @@ -15,16 +15,16 @@ class MainActivity : AppCompatActivity() { when (item.itemId) { tab_last_match -> { val matchFragment = MatchFragment.newInstance(0) - loadMatchFragment(savedInstanceState, matchFragment) + loadMatchFragment(matchFragment) } tab_next_match -> { val matchFragment = MatchFragment.newInstance(1) - loadMatchFragment(savedInstanceState, matchFragment) + loadMatchFragment(matchFragment) } favorites -> { - loadFavoritesFragment(savedInstanceState) + loadFavoritesFragment() } } true @@ -33,17 +33,14 @@ class MainActivity : AppCompatActivity() { } - private fun loadFavoritesFragment(savedInstanceState: Bundle?) { + private fun loadFavoritesFragment() { supportFragmentManager .beginTransaction() .replace(R.id.main_container, FavoriteMatchesFragment(), FavoriteMatchesFragment::class.java.simpleName) .commit() } - private fun loadMatchFragment( - savedInstanceState: Bundle?, - matchFragment: MatchFragment - ) { + private fun loadMatchFragment(matchFragment: MatchFragment) { supportFragmentManager .beginTransaction() .replace(R.id.main_container, matchFragment, MatchFragment::class.java.simpleName) diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MatchFragment.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MatchFragment.kt index daeddbb..293885e 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MatchFragment.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/MatchFragment.kt @@ -1,6 +1,7 @@ package com.example.wijaya_pc.eplmatchschedule import android.content.Context +import android.content.Intent import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.widget.SwipeRefreshLayout @@ -56,6 +57,10 @@ class MatchFragment() : Fragment(), AnkoComponent, MainView { "homeTeamId" to "${it.idHomeTeam}", "awayTeamId" to "${it.idAwayTeam}" ) + + /* val intent = Intent(ctx, DetailActivity::class.java) + intent.putExtra(DetailActivity.matchParcel, it) + startActivity(intent)*/ } listMatch.adapter = adapter @@ -103,6 +108,7 @@ class MatchFragment() : Fragment(), AnkoComponent, MainView { lparams(width = matchParent, height = wrapContent) listMatch = recyclerView { + id = R.id.listMatch lparams(width = matchParent, height = wrapContent) layoutManager = LinearLayoutManager(ctx) } diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/Utils.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/Utils.kt index 4826fee..fcc52c9 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/Utils.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/Utils.kt @@ -2,6 +2,7 @@ package com.example.wijaya_pc.eplmatchschedule import android.view.View import java.text.SimpleDateFormat +import java.util.* //untuk menampilkan ProgressBar fun View.visible() { @@ -13,4 +14,6 @@ fun View.invisible() { visibility = View.INVISIBLE } -val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy") +fun dateToSimpleString(date: Date?): String? = with(date ?: Date()) { + SimpleDateFormat("EEE, dd MMM yyyy").format(this) +} diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/adapter/MatchAdapter.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/adapter/MatchAdapter.kt index 2a62859..23f7619 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/adapter/MatchAdapter.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/adapter/MatchAdapter.kt @@ -7,7 +7,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import com.example.wijaya_pc.eplmatchschedule.R.id.* -import com.example.wijaya_pc.eplmatchschedule.dateFormat +import com.example.wijaya_pc.eplmatchschedule.dateToSimpleString import com.example.wijaya_pc.eplmatchschedule.model.Match import com.example.wijaya_pc.eplmatchschedule.ui.MatchUI import org.jetbrains.anko.AnkoContext @@ -39,7 +39,7 @@ class MatchViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val matchAwayTeam: TextView = view.find(match_away_team) fun bindItem(matches: Match, listener: (Match) -> Unit) { - matchDate.text = dateFormat.format(matches.matchDate).toString() + matchDate.text = dateToSimpleString(matches.matchDate) matchHomeTeam.text = matches.homeTeam matchHomeScore.text = matches.homeScore diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/api/TheSportDBApi.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/api/TheSportDBApi.kt index 86531b4..f17adac 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/api/TheSportDBApi.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/api/TheSportDBApi.kt @@ -6,50 +6,18 @@ import com.example.wijaya_pc.eplmatchschedule.BuildConfig object TheSportDBApi { fun getLast15Matches(leagueID: String?): String { - return Uri.parse(BuildConfig.BASE_URL).buildUpon() - .appendPath("api") - .appendPath("v1") - .appendPath("json") - .appendPath(BuildConfig.TSDB_API_KEY) - .appendPath("eventspastleague.php") - .appendQueryParameter("id", leagueID) - .build() - .toString() + return BuildConfig.BASE_URL + "api/v1/json/${BuildConfig.TSDB_API_KEY}" + "/eventspastleague.php?id=" + leagueID } fun getNext15Matches(leagueID: String?): String { - return Uri.parse(BuildConfig.BASE_URL).buildUpon() - .appendPath("api") - .appendPath("v1") - .appendPath("json") - .appendPath(BuildConfig.TSDB_API_KEY) - .appendPath("eventsnextleague.php") - .appendQueryParameter("id", leagueID) - .build() - .toString() + return BuildConfig.BASE_URL + "api/v1/json/${BuildConfig.TSDB_API_KEY}" + "/eventsnextleague.php?id=" + leagueID } fun getMatchDetail(matchID: String?): String { - return Uri.parse(BuildConfig.BASE_URL).buildUpon() - .appendPath("api") - .appendPath("v1") - .appendPath("json") - .appendPath(BuildConfig.TSDB_API_KEY) - .appendPath("lookupevent.php") - .appendQueryParameter("id", matchID) - .build() - .toString() + return BuildConfig.BASE_URL + "api/v1/json/${BuildConfig.TSDB_API_KEY}" + "/lookupevent.php?id=" + matchID } fun getTeam(teamID: String?): String { - return Uri.parse(BuildConfig.BASE_URL).buildUpon() - .appendPath("api") - .appendPath("v1") - .appendPath("json") - .appendPath(BuildConfig.TSDB_API_KEY) - .appendPath("lookupteam.php") - .appendQueryParameter("id", teamID) - .build() - .toString() + return BuildConfig.BASE_URL + "api/v1/json/${BuildConfig.TSDB_API_KEY}" + "/lookupteam.php?id=" + teamID } } \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/CoroutineContextProvider.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/CoroutineContextProvider.kt new file mode 100644 index 0000000..0c9c669 --- /dev/null +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/CoroutineContextProvider.kt @@ -0,0 +1,8 @@ +package com.example.wijaya_pc.eplmatchschedule.coroutine + +import kotlinx.coroutines.experimental.android.UI +import kotlin.coroutines.experimental.CoroutineContext + +open class CoroutineContextProvider { + open val main: CoroutineContext by lazy { UI } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/TestContextProvider.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/TestContextProvider.kt new file mode 100644 index 0000000..95c88dd --- /dev/null +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/coroutine/TestContextProvider.kt @@ -0,0 +1,8 @@ +package com.example.wijaya_pc.eplmatchschedule.coroutine + +import kotlinx.coroutines.experimental.Unconfined +import kotlin.coroutines.experimental.CoroutineContext + +class TestContextProvider : CoroutineContextProvider() { + override val main: CoroutineContext = Unconfined +} \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Match.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Match.kt index ede51f7..0b52e98 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Match.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Match.kt @@ -7,51 +7,28 @@ import java.util.* @Parcelize data class Match( - @SerializedName("idEvent") var matchId: String? = null, - - @SerializedName("dateEvent") var matchDate: Date? = null, - - @SerializedName("idHomeTeam") var idHomeTeam: String? = null, - - @SerializedName("idAwayTeam") var idAwayTeam: String? = null, - - @SerializedName("strHomeTeam") var homeTeam: String? = null, - - @SerializedName("strAwayTeam") var awayTeam: String? = null, - - @SerializedName("intHomeScore") var homeScore: String? = null, - - @SerializedName("intAwayScore") var awayScore: String? = null, - - @SerializedName("strHomeFormation") var homeFormation: String? = null, - - @SerializedName("strAwayFormation") var awayFormation: String? = null, - - @SerializedName("strHomeGoalDetails") var homeGoals: String? = null, - - @SerializedName("strAwayGoalDetails") var awayGoals: String? = null, - - @SerializedName("intHomeShots") var homeShots: String? = null, - - @SerializedName("intAwayShots") var awayShots: String? = null, - - @SerializedName("strHomeLineupGoalkeeper") var homeGoalKeeper: String? = null, - - @SerializedName("strAwayLineupGoalkeeper") var awayGoalKeeper: String? = null, - - @SerializedName("strHomeLineupDefense") var homeDefence: String? = null, - - @SerializedName("strAwayLineupDefense") var awayDefence: String? = null, - - @SerializedName("strHomeLineupMidfield") var homeMidfield: String? = null, - - @SerializedName("strAwayLineupMidfield") var awayMidfield: String? = null, - - @SerializedName("strHomeLineupForward") var homeForward: String? = null, - - @SerializedName("strAwayLineupForward") var awayForward: String? = null, - - @SerializedName("strHomeLineupSubstitutes") var homeSubstitutes: String? = null, - - @SerializedName("strAwayLineupSubstitutes") var awaySubtitutes: String? = null + @SerializedName("idEvent") var matchId: String? , + @SerializedName("dateEvent") var matchDate: Date? , + @SerializedName("idHomeTeam") var idHomeTeam: String? , + @SerializedName("idAwayTeam") var idAwayTeam: String? , + @SerializedName("strHomeTeam") var homeTeam: String? , + @SerializedName("strAwayTeam") var awayTeam: String? , + @SerializedName("intHomeScore") var homeScore: String? , + @SerializedName("intAwayScore") var awayScore: String? , + @SerializedName("strHomeFormation") var homeFormation: String? , + @SerializedName("strAwayFormation") var awayFormation: String? , + @SerializedName("strHomeGoalDetails") var homeGoals: String? , + @SerializedName("strAwayGoalDetails") var awayGoals: String? , + @SerializedName("intHomeShots") var homeShots: String? , + @SerializedName("intAwayShots") var awayShots: String? , + @SerializedName("strHomeLineupGoalkeeper") var homeGoalKeeper: String? , + @SerializedName("strAwayLineupGoalkeeper") var awayGoalKeeper: String? , + @SerializedName("strHomeLineupDefense") var homeDefence: String? , + @SerializedName("strAwayLineupDefense") var awayDefence: String? , + @SerializedName("strHomeLineupMidfield") var homeMidfield: String? , + @SerializedName("strAwayLineupMidfield") var awayMidfield: String? , + @SerializedName("strHomeLineupForward") var homeForward: String? , + @SerializedName("strAwayLineupForward") var awayForward: String? , + @SerializedName("strHomeLineupSubstitutes") var homeSubstitutes: String? , + @SerializedName("strAwayLineupSubstitutes") var awaySubtitutes: String? ) : Parcelable diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Team.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Team.kt index 73e37fc..ecd7116 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Team.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/model/Team.kt @@ -4,11 +4,11 @@ import com.google.gson.annotations.SerializedName data class Team( @SerializedName("idTeam") - var teamId: String? = null, + var teamId: String? , @SerializedName("strTeam") - var teamName: String? = null, + var teamName: String? , @SerializedName("strTeamBadge") - var teamBadge: String? = null + var teamBadge: String? ) diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenter.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenter.kt index c23974a..c5ee310 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenter.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenter.kt @@ -1,48 +1,97 @@ package com.example.wijaya_pc.eplmatchschedule.presenter +import android.content.Context +import android.database.sqlite.SQLiteConstraintException +import android.util.Log import com.example.wijaya_pc.eplmatchschedule.api.ApiRepository import com.example.wijaya_pc.eplmatchschedule.api.TheSportDBApi +import com.example.wijaya_pc.eplmatchschedule.coroutine.CoroutineContextProvider +import com.example.wijaya_pc.eplmatchschedule.database.database +import com.example.wijaya_pc.eplmatchschedule.model.FavoriteMatches import com.example.wijaya_pc.eplmatchschedule.model.MatchResponse import com.example.wijaya_pc.eplmatchschedule.model.TeamResponse import com.example.wijaya_pc.eplmatchschedule.view.DetailView import com.google.gson.Gson -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread +import kotlinx.coroutines.experimental.async +import org.jetbrains.anko.coroutines.experimental.bg +import org.jetbrains.anko.db.delete +import org.jetbrains.anko.db.insert + class DetailPresenter( private val view: DetailView, private val apiRepository: ApiRepository, - private val gson: Gson + private val gson: Gson, + private val context: CoroutineContextProvider = CoroutineContextProvider() ) { - fun getMatchDetail(matchID: String?) { view.showLoading() - doAsync { - val data = gson.fromJson( - apiRepository.doRequest(TheSportDBApi.getMatchDetail(matchID)), - MatchResponse::class.java - ) - - uiThread { - view.hideLoading() - view.getMatch(data.events[0]) + + async(context.main) { + val data = bg { + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getMatchDetail(matchID)), + MatchResponse::class.java + ) } + view.getMatch(data.await().events[0]) + view.hideLoading() } + } fun getTeamDetail(teamID: String?, homeTeam: Boolean) { view.showLoading() - doAsync { - val data = gson.fromJson( - apiRepository.doRequest(TheSportDBApi.getTeam(teamID)), - TeamResponse::class.java - ) - - uiThread { - view.hideLoading() - view.getTeam(data.teams[0], homeTeam) + + async(context.main) { + val data = bg { + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getTeam(teamID)), + TeamResponse::class.java + ) + } + view.getTeam(data.await().teams[0], homeTeam) + view.hideLoading() + } + } + + fun addToFavorite( + context: Context, + matchID: String?, + matchDate: String?, + matchIDHomeTeam: String?, + matchIDAwayTeam: String?, + homeTeam: String?, + awayTeam: String?, + homeScore: String?, + awayScore: String? + ) { + try { + context.database.use { + insert( + FavoriteMatches.TABLE_FAVORITE_MATCH, + FavoriteMatches.MATCH_ID to matchID, + FavoriteMatches.MATCH_DATE to matchDate, + FavoriteMatches.HOME_TEAM_ID to matchIDHomeTeam, + FavoriteMatches.AWAY_TEAM_ID to matchIDAwayTeam, + FavoriteMatches.HOME_TEAM_NAME to homeTeam, + FavoriteMatches.AWAY_TEAM_NAME to awayTeam, + FavoriteMatches.HOME_TEAM_SCORE to homeScore, + FavoriteMatches.AWAY_TEAM_SCORE to awayScore + ) } + } catch (e: SQLiteConstraintException) { + Log.d("failed insert fav", e.localizedMessage) } + } + fun removeFromFavorite(context: Context, matchID: String?) { + try { + context.database.use { + delete(FavoriteMatches.TABLE_FAVORITE_MATCH, "(MATCH_ID = {id})", "id" to "$matchID") + } + } catch (e: SQLiteConstraintException) { + Log.d("failed delete fav", e.localizedMessage) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenter.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenter.kt index fab2ae4..d9aaaea 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenter.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenter.kt @@ -2,48 +2,48 @@ package com.example.wijaya_pc.eplmatchschedule.presenter import com.example.wijaya_pc.eplmatchschedule.api.ApiRepository import com.example.wijaya_pc.eplmatchschedule.api.TheSportDBApi +import com.example.wijaya_pc.eplmatchschedule.coroutine.CoroutineContextProvider import com.example.wijaya_pc.eplmatchschedule.model.MatchResponse import com.example.wijaya_pc.eplmatchschedule.view.MainView import com.google.gson.Gson -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread +import kotlinx.coroutines.experimental.async +import org.jetbrains.anko.coroutines.experimental.bg class MainPresenter( private val view: MainView, private val apiRepository: ApiRepository, - private val gson: Gson + private val gson: Gson, + private val context: CoroutineContextProvider = CoroutineContextProvider() ) { fun getLast15MatchesList(leagueId: String?) { //Log.d("linknya", TheSportDBApi.getLast15Matches(leagueId)) view.showLoading() - doAsync { - val data = gson.fromJson( - apiRepository.doRequest(TheSportDBApi.getLast15Matches(leagueId)), - MatchResponse::class.java - ) - // Log.e("the data", "$data") - uiThread { - view.hideLoading() - view.showMatchList(data.events) + async(context.main) { + val data = bg { + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getLast15Matches(leagueId)), + MatchResponse::class.java + ) } + view.showMatchList(data.await().events) + view.hideLoading() } } fun getNext15MatchesList(leagueId: String?) { //Log.d("linknya", TheSportDBApi.getLast15Matches(leagueId)) view.showLoading() - doAsync { - val data = gson.fromJson( - apiRepository.doRequest(TheSportDBApi.getNext15Matches(leagueId)), - MatchResponse::class.java - ) - // Log.e("the data", "$data") - uiThread { - view.hideLoading() - view.showMatchList(data.events) + async(context.main) { + val data = bg { + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getNext15Matches(leagueId)), + MatchResponse::class.java + ) } + view.showMatchList(data.await().events) + view.hideLoading() } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/view/DetailView.kt b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/view/DetailView.kt index 9fcd76a..b30ccda 100644 --- a/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/view/DetailView.kt +++ b/app/src/main/java/com/example/wijaya_pc/eplmatchschedule/view/DetailView.kt @@ -8,9 +8,4 @@ interface DetailView { fun hideLoading() fun getMatch(data: Match) fun getTeam(data: Team, homeTeam: Boolean) - fun addToFavorite() - fun removeFromFavorite() - fun setFavorite() - fun favoriteState() - } \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 4fe971f..b33087b 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -28,6 +28,8 @@ + + \ No newline at end of file diff --git a/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/UtilsKtTest.kt b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/UtilsKtTest.kt new file mode 100644 index 0000000..f767e83 --- /dev/null +++ b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/UtilsKtTest.kt @@ -0,0 +1,15 @@ +package com.example.wijaya_pc.eplmatchschedule + +import org.junit.Test + +import org.junit.Assert.* +import java.text.SimpleDateFormat + +class UtilsKtTest { + + @Test + fun testDateToSimpleString() { + val date = SimpleDateFormat("MM/dd/yyyy").parse("10/25/2018") + assertEquals("Thu, 25 Oct 2018", dateToSimpleString(date)) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/api/ApiRepositoryTest.kt b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/api/ApiRepositoryTest.kt new file mode 100644 index 0000000..1559054 --- /dev/null +++ b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/api/ApiRepositoryTest.kt @@ -0,0 +1,18 @@ +package com.example.wijaya_pc.eplmatchschedule.api + +import org.junit.Test + +import org.junit.Assert.* +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify + +class ApiRepositoryTest { + + @Test + fun testDoRequest() { + val apiRepository = mock(ApiRepository::class.java) + val url = "https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League" + apiRepository.doRequest(url) + verify(apiRepository).doRequest(url) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenterTest.kt b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenterTest.kt new file mode 100644 index 0000000..d7fbd0e --- /dev/null +++ b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/DetailPresenterTest.kt @@ -0,0 +1,116 @@ +package com.example.wijaya_pc.eplmatchschedule.presenter + +import com.example.wijaya_pc.eplmatchschedule.api.ApiRepository +import com.example.wijaya_pc.eplmatchschedule.api.TheSportDBApi +import com.example.wijaya_pc.eplmatchschedule.coroutine.TestContextProvider +import com.example.wijaya_pc.eplmatchschedule.model.Match +import com.example.wijaya_pc.eplmatchschedule.model.MatchResponse +import com.example.wijaya_pc.eplmatchschedule.model.Team +import com.example.wijaya_pc.eplmatchschedule.model.TeamResponse +import com.example.wijaya_pc.eplmatchschedule.view.DetailView +import com.google.gson.Gson +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.text.SimpleDateFormat + +class DetailPresenterTest { + + @Mock + private + lateinit var view: DetailView + + @Mock + private + lateinit var apiRepository: ApiRepository + + @Mock + private + lateinit var gson: Gson + + private lateinit var presenter: DetailPresenter + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + presenter = DetailPresenter(view, apiRepository, gson, TestContextProvider()) + } + + @Test + fun testGetMatchDetail() { + val matches: List = listOf( + Match( + "576554", + SimpleDateFormat("MM/dd/yyyy").parse("2018/10/20"), + "133610", + "133612", + "Chelsea", + "Man United", + "2", + "2", + null, + null, + "21':Antonio Ruediger;90':Ross Barkley;", + "55':Anthony Martial;73':Anthony Martial;", + "6", + "4", + "Kepa Arrizabalaga; ", + "David De Gea; ", + "Cesar Azpilicueta; Antonio Ruediger; David Luiz; Marcos Alonso; ", + "Ashley Young; Chris Smalling; Victor Nilsson Lindeloef; Luke Shaw; ", + "N'Golo Kante; Jorginho; Mateo Kovacic; ", + "Nemanja Matic; Paul Pogba; Marcus Rashford; Juan Mata; Anthony Martial; ", + "Willian; Alvaro Morata; Eden Hazard; ", + "Romelu Lukaku; ", + "Wilfredo Caballero; Gary Cahill; Davide Zappacosta; Cesc Fabregas; Pedro Rodriguez; Olivier Giroud; Ross Barkley; ", + "Sergio Romero; Eric Bailly; Matteo Darmian; Andreas Pereira; Ander Herrera; Fred; Alexis Sanchez; " + ) + ) + val response = MatchResponse(matches) + val matchId = "576554" + + `when`( + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getMatchDetail(matchId)), + MatchResponse::class.java + ) + ).thenReturn(response) + + presenter.getMatchDetail(matchId) + + verify(view).showLoading() + verify(view).getMatch(matches[0]) + verify(view).hideLoading() + } + + @Test + fun testGetTeamDetail() { + val teamId = "133610" + val team: List = listOf( + Team( + "133610", + "Chelsea", + "https://www.thesportsdb.com/images/media/team/badge/yvwvtu1448813215.png" + ) + ) + val response = TeamResponse(team) + val homeTeam = false + + `when`( + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getTeam(teamId)), + TeamResponse::class.java + ) + ).thenReturn(response) + + presenter.getTeamDetail(teamId, homeTeam) + + verify(view).showLoading() + verify(view).getTeam(team[0], homeTeam) + verify(view).hideLoading() + + } +} \ No newline at end of file diff --git a/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenterTest.kt b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenterTest.kt new file mode 100644 index 0000000..e0e2b9f --- /dev/null +++ b/app/src/test/java/com/example/wijaya_pc/eplmatchschedule/presenter/MainPresenterTest.kt @@ -0,0 +1,80 @@ +package com.example.wijaya_pc.eplmatchschedule.presenter + +import com.example.wijaya_pc.eplmatchschedule.api.ApiRepository +import com.example.wijaya_pc.eplmatchschedule.api.TheSportDBApi +import com.example.wijaya_pc.eplmatchschedule.coroutine.TestContextProvider +import com.example.wijaya_pc.eplmatchschedule.model.Match +import com.example.wijaya_pc.eplmatchschedule.model.MatchResponse +import com.example.wijaya_pc.eplmatchschedule.view.MainView +import com.google.gson.Gson +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +class MainPresenterTest { + + @Mock + private + lateinit var view: MainView + + @Mock + private + lateinit var apiRepository: ApiRepository + + @Mock + private + lateinit var gson: Gson + + + private lateinit var presenter: MainPresenter + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + presenter = MainPresenter(view, apiRepository, gson, TestContextProvider()) + } + + @Test + fun testGetLast15MatchesList() { + val matches: MutableList = mutableListOf() + val response = MatchResponse(matches) + val leagueId = "4328" + + `when`( + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getLast15Matches(leagueId)), + MatchResponse::class.java + ) + ).thenReturn(response) + + presenter.getLast15MatchesList(leagueId) + + verify(view).showLoading() + verify(view).showMatchList(matches) + verify(view).hideLoading() + + } + + @Test + fun testGetNext15MatchesList() { + val matches: MutableList = mutableListOf() + val response = MatchResponse(matches) + val leagueId = "4328" + + `when`( + gson.fromJson( + apiRepository.doRequest(TheSportDBApi.getNext15Matches(leagueId)), + MatchResponse::class.java + ) + ).thenReturn(response) + + presenter.getNext15MatchesList(leagueId) + + verify(view).showLoading() + verify(view).showMatchList(matches) + verify(view).hideLoading() + } +} \ No newline at end of file