Skip to content

Commit

Permalink
Merge branch 'release/2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanmedack committed Jan 3, 2018
2 parents 836d7bd + d18a173 commit ce5dd90
Show file tree
Hide file tree
Showing 42 changed files with 1,284 additions and 497 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## 2.1.0

- improves seeking workaround
- split up conferences and events
- dedicated event list section

## 2.0.0

- adds streaming
Expand Down
13 changes: 7 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ext {
rxBindingVersion = "2.0.0"
rxJavaVersion = "2.1.4"
rxKotlinVersion = "2.1.0"
supportLibVersion = "26.1.0"
supportLibVersion = "27.0.2"
timberVersion = "4.5.1"

// Test dependencies
Expand All @@ -35,14 +35,14 @@ ext {

android {

compileSdkVersion 26
buildToolsVersion '26.0.2'
compileSdkVersion 27
buildToolsVersion '27.0.1'
defaultConfig {
applicationId "de.stefanmedack.ccctv"
minSdkVersion 21
targetSdkVersion 26
versionCode 9
versionName "2.0.0"
targetSdkVersion 27
versionCode 10
versionName "2.1.0"
resConfigs "en", "de"
multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -102,6 +102,7 @@ dependencies {

// Support Libs
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:leanback-v17:$supportLibVersion"

// Architecture Components - View Model (+ Lifecycles, LiveData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class ConferenceDaoTest : BaseDbTest() {
fun insert_and_retrieve_minimal_conference() {
db.conferenceDao().insert(minimalConferenceEntity)

val loadedConferences = db.conferenceDao().getConferenceById(minimalConferenceEntity.id).test()
val loadedConference = db.conferenceDao().getConferenceById(minimalConferenceEntity.id).test()

loadedConferences.assertValue(minimalConferenceEntity)
loadedConference.assertValue(minimalConferenceEntity)
}

@Test
Expand Down Expand Up @@ -66,6 +66,20 @@ class ConferenceDaoTest : BaseDbTest() {
assertEquals(loadedConferences[0], newConference)
}

@Test
fun insert_and_retrieve_multiple_conferences_filtered_by_group() {
val conferences = listOf(
minimalConferenceEntity.copy(id = 1, slug = "congress/33c3"),
fullConferenceEntity.copy(id = 2, slug = "not_congress/droidcon")
)

db.conferenceDao().insertAll(conferences)
val loadedConferences = db.conferenceDao().getConferences("congress").test().values()[0]

assertEquals(loadedConferences.size, 1)
assertEquals(loadedConferences[0], conferences[0])
}

// Conferences with Events

@Test
Expand Down Expand Up @@ -103,7 +117,7 @@ class ConferenceDaoTest : BaseDbTest() {
assertEquals(loadedConferences[1].events, listOf<Event>())
}

@Test
@Test
fun insert_and_retrieve_multiple_conferences_with_events_in_single_insert() {
val conferences = listOf(
minimalConferenceEntity.copy(id = 1),
Expand Down Expand Up @@ -137,4 +151,17 @@ class ConferenceDaoTest : BaseDbTest() {
assertEquals(loadedConferences.size, 1)
assertEquals(loadedConferences[0].conference, conferences[0])
}

@Test
fun insert_and_retrieve_multiple_conferences_with_events_by_conference_id() {
val conferences = listOf(
minimalConferenceEntity.copy(id = 1, slug = "congress/33c3"),
fullConferenceEntity.copy(id = 2, slug = "not_congress/droidcon")
)

db.conferenceDao().insertAll(conferences)
val loadedConference = db.conferenceDao().getConferenceWithEventsById(2).test()

loadedConference.assertValue(ConferenceWithEvents(fullConferenceEntity, listOf()))
}
}
8 changes: 7 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@
</intent-filter>
</activity>

<activity
android:name=".ui.events.EventsActivity"
android:theme="@style/EventsActivityTheme"
/>

<activity android:name=".ui.detail.DetailActivity"/>

<activity android:name=".ui.search.SearchActivity"/>

<activity
Expand All @@ -92,7 +98,7 @@
/>

<activity
android:name=".ui.playback.ExoPlayerActivity"
android:name=".ui.streaming.StreamingPlayerActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|layoutDirection"
android:exported="true"
android:launchMode="singleTask"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@ import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.stefanmedack.ccctv.ui.detail.DetailActivity
import de.stefanmedack.ccctv.ui.detail.DetailModule
import de.stefanmedack.ccctv.ui.events.EventsActivity
import de.stefanmedack.ccctv.ui.events.EventsModule
import de.stefanmedack.ccctv.ui.main.MainActivity
import de.stefanmedack.ccctv.ui.main.MainModule
import de.stefanmedack.ccctv.ui.search.SearchActivity
import de.stefanmedack.ccctv.ui.search.SearchModule

@Module(includes = arrayOf(ViewModelModule::class))
@Module(includes = [
ViewModelModule::class
])
abstract class ActivityBuilderModule {

@ContributesAndroidInjector(modules = arrayOf(MainModule::class))
@ContributesAndroidInjector(modules = [MainModule::class])
abstract fun contributeMain(): MainActivity

@ContributesAndroidInjector(modules = arrayOf(DetailModule::class))
@ContributesAndroidInjector(modules = [EventsModule::class])
abstract fun contributeEvents(): EventsActivity

@ContributesAndroidInjector(modules = [DetailModule::class])
abstract fun contributeDetail(): DetailActivity

@ContributesAndroidInjector(modules = arrayOf(SearchModule::class))
@ContributesAndroidInjector(modules = [SearchModule::class])
abstract fun contributeSearch(): SearchActivity

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import dagger.multibindings.IntoMap
import de.stefanmedack.ccctv.di.C3ViewModelFactory
import de.stefanmedack.ccctv.di.Scopes.ViewModelKey
import de.stefanmedack.ccctv.ui.detail.DetailViewModel
import de.stefanmedack.ccctv.ui.main.GroupedConferencesViewModel
import de.stefanmedack.ccctv.ui.events.EventsViewModel
import de.stefanmedack.ccctv.ui.main.ConferencesViewModel
import de.stefanmedack.ccctv.ui.main.LiveStreamingViewModel
import de.stefanmedack.ccctv.ui.main.MainViewModel
import de.stefanmedack.ccctv.ui.search.SearchViewModel
Expand All @@ -23,8 +24,8 @@ abstract class ViewModelModule {

@Binds
@IntoMap
@ViewModelKey(GroupedConferencesViewModel::class)
abstract fun bindGroupedConferencesViewModel(viewModel: GroupedConferencesViewModel): ViewModel
@ViewModelKey(ConferencesViewModel::class)
abstract fun bindConferencesViewModel(viewModel: ConferencesViewModel): ViewModel

@Binds
@IntoMap
Expand All @@ -36,6 +37,11 @@ abstract class ViewModelModule {
@ViewModelKey(DetailViewModel::class)
abstract fun bindDetailViewModel(viewModel: DetailViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(EventsViewModel::class)
abstract fun bindEventsViewModel(viewModel: EventsViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(SearchViewModel::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ interface ConferenceDao {
@Query("SELECT * FROM Conferences")
fun getConferences(): Flowable<List<Conference>>

@Query("SELECT * FROM Conferences WHERE slug LIKE :conferenceGroup || '%'")
fun getConferences(conferenceGroup: String): Flowable<List<Conference>>

@Query("SELECT * FROM Conferences WHERE id = :id")
fun getConferenceById(id: Int): Flowable<Conference>

@Query("SELECT * FROM Conferences")
fun getConferencesWithEvents(): Flowable<List<ConferenceWithEvents>>

@Query("SELECT * FROM Conferences WHERE slug LIKE :conferenceGroup || '%'")
fun getConferencesWithEvents(conferenceGroup: String): Flowable<List<ConferenceWithEvents>>

@Query("SELECT * FROM Conferences WHERE id = :id")
fun getConferenceById(id: Int): Flowable<Conference>
fun getConferenceWithEventsById(id: Int): Flowable<ConferenceWithEvents>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(conference: Conference)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.arch.persistence.room.OnConflictStrategy
import android.arch.persistence.room.Query
import de.stefanmedack.ccctv.persistence.entities.Event
import io.reactivex.Flowable
import io.reactivex.Single

@Dao
interface EventDao {
Expand All @@ -17,7 +18,7 @@ interface EventDao {
fun getEvents(ids: List<Int>): Flowable<List<Event>>

@Query("SELECT * FROM Events WHERE id = :id")
fun getEventById(id: Int): Flowable<Event>
fun getEventById(id: Int): Single<Event>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(event: Event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ import org.threeten.bp.LocalDate
import org.threeten.bp.OffsetDateTime

@Entity(tableName = "events",
foreignKeys = arrayOf(
ForeignKey(
entity = Conference::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("conference_id"),
onDelete = CASCADE
))
)
foreignKeys = [ForeignKey(
entity = Conference::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("conference_id"),
onDelete = CASCADE
)])
data class Event(

@PrimaryKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.stefanmedack.ccctv.repository

import de.stefanmedack.ccctv.model.Resource
import de.stefanmedack.ccctv.persistence.daos.ConferenceDao
import de.stefanmedack.ccctv.persistence.daos.EventDao
import de.stefanmedack.ccctv.persistence.entities.ConferenceWithEvents
import de.stefanmedack.ccctv.persistence.preferences.C3SharedPreferences
import de.stefanmedack.ccctv.persistence.separateLists
Expand All @@ -18,29 +19,58 @@ import javax.inject.Singleton
class ConferenceRepository @Inject constructor(
private val mediaService: RxC3MediaService,
private val conferenceDao: ConferenceDao,
private val eventDao: EventDao,
private val preferences: C3SharedPreferences
) {
// TODO are plain conferences still needed?
// val conferences: Flowable<Resource<List<ConferenceEntity>>>
// get() = object : NetworkBoundResource<List<ConferenceEntity>, List<ConferenceRemote>>() {
//
// override fun fetchLocal(): Flowable<List<ConferenceEntity>> = conferenceDao.getConferences()
//
// override fun saveLocal(data: List<ConferenceEntity>) = conferenceDao.insertAll(data)
//
// override fun isStale(localResource: Resource<List<ConferenceEntity>>) = when (localResource) {
// is Resource.Error -> true
// is Resource.Loading -> false
// is Resource.Success -> localResource.data.isEmpty()
// }
//
// override fun fetchNetwork(): Single<List<ConferenceRemote>> = mediaService
// .getConferences()
// .map { it.conferences?.filterNotNull() }
//
// override fun mapNetworkToLocal(data: List<ConferenceRemote>) = data.mapNotNull { it.toEntity() }
//
// }.resource
val conferences: Flowable<Resource<List<ConferenceEntity>>>
get() = object : NetworkBoundResource<List<ConferenceEntity>, List<ConferenceRemote>>() {

override fun fetchLocal(): Flowable<List<ConferenceEntity>> = conferenceDao.getConferences()

override fun saveLocal(data: List<ConferenceEntity>) = conferenceDao.insertAll(data)

override fun isStale(localResource: Resource<List<ConferenceEntity>>) = when (localResource) {
is Resource.Error -> true
is Resource.Loading -> false
is Resource.Success -> localResource.data.isEmpty()
}

override fun fetchNetwork(): Single<List<ConferenceRemote>> = mediaService
.getConferences()
.map { it.conferences?.filterNotNull() }

override fun mapNetworkToLocal(data: List<ConferenceRemote>) = data.mapNotNull { it.toEntity() }

}.resource

fun conferenceWithEvents(conferenceId: Int): Flowable<Resource<ConferenceWithEvents>>
= object : NetworkBoundResource<ConferenceWithEvents, ConferenceRemote>() {

override fun fetchLocal(): Flowable<ConferenceWithEvents> = conferenceDao.getConferenceWithEventsById(conferenceId)

override fun saveLocal(data: ConferenceWithEvents) =
data.let { (_, events) ->
eventDao.insertAll(events)
preferences.updateLatestDataFetchDate()
}

override fun isStale(localResource: Resource<ConferenceWithEvents>) = when (localResource) {
is Resource.Success -> localResource.data.events.isEmpty() || preferences.isFetchedDataStale()
is Resource.Loading -> false
is Resource.Error -> true
}

override fun fetchNetwork(): Single<ConferenceRemote> = mediaService.getConference(conferenceId)
.applySchedulers()

override fun mapNetworkToLocal(data: ConferenceRemote): ConferenceWithEvents =
data.toEntity()?.let { conference ->
ConferenceWithEvents(
conference = conference,
events = data.events?.mapNotNull { it?.toEntity(conferenceId) } ?: listOf()
)
} ?: throw IllegalArgumentException("Could not parse ConferenceRemote to ConferenceEntity")
}.resource

val conferencesWithEvents: Flowable<Resource<List<ConferenceWithEvents>>>
get() = object : NetworkBoundResource<List<ConferenceWithEvents>, List<ConferenceRemote>>() {
Expand Down Expand Up @@ -84,9 +114,9 @@ class ConferenceRepository @Inject constructor(

}.resource

fun loadedConferences(conferenceGroup: String): Flowable<Resource<List<ConferenceWithEvents>>> = conferenceDao
.getConferencesWithEvents(conferenceGroup.toLowerCase())
.map<Resource<List<ConferenceWithEvents>>> { Resource.Success(it) }
fun loadedConferences(conferenceGroup: String): Flowable<Resource<List<ConferenceEntity>>> = conferenceDao
.getConferences(conferenceGroup.toLowerCase())
.map<Resource<List<ConferenceEntity>>> { Resource.Success(it) }
.applySchedulers()

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.stefanmedack.ccctv.repository

import de.stefanmedack.ccctv.persistence.daos.EventDao
import de.stefanmedack.ccctv.persistence.entities.Event
import de.stefanmedack.ccctv.persistence.toEntity
import de.stefanmedack.ccctv.util.applySchedulers
import info.metadude.kotlin.library.c3media.RxC3MediaService
import io.reactivex.Flowable
Expand All @@ -15,7 +16,11 @@ class EventRepository @Inject constructor(
private val eventDao: EventDao
) {
fun getEvent(id: Int): Flowable<Event> = eventDao.getEventById(id)
.applySchedulers()
.onErrorResumeNext(
mediaService.getEvent(id)
.applySchedulers()
.map { it.toEntity(-1)!! }
).toFlowable()

fun getEvents(ids: List<Int>): Flowable<List<Event>> = eventDao.getEvents(ids)
.applySchedulers()
Expand Down
Loading

0 comments on commit ce5dd90

Please sign in to comment.