mirror of
https://github.com/NiciDieNase/chaosflix
synced 2025-02-20 23:18:27 +00:00
remove RxJava and move Downloader from touch-project
This commit is contained in:
parent
14609cc907
commit
c2a83fae2d
14 changed files with 376 additions and 41 deletions
common
build.gradle
src/main
java/de/nicidienase/chaosflix/common
mediadata
entities/recording
network
sync
util
res/values
|
@ -45,12 +45,9 @@ dependencies {
|
||||||
api "android.arch.persistence.room:rxjava2:${rootProject.ext.archCompVersion}"
|
api "android.arch.persistence.room:rxjava2:${rootProject.ext.archCompVersion}"
|
||||||
kapt "android.arch.persistence.room:compiler:${rootProject.ext.archCompVersion}"
|
kapt "android.arch.persistence.room:compiler:${rootProject.ext.archCompVersion}"
|
||||||
|
|
||||||
api 'io.reactivex.rxjava2:rxjava:2.1.5'
|
|
||||||
|
|
||||||
api 'com.squareup.retrofit2:retrofit:2.3.0'
|
api 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||||
|
|
||||||
api 'com.squareup.retrofit2:converter-gson:2.3.0'
|
api 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||||
api 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
|
|
||||||
|
|
||||||
api 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.0'
|
api 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.0'
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,14 @@ data class Recording(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var recordingID: Long
|
var recordingID: Long
|
||||||
var eventID: Long
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val strings = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
val strings = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
recordingID = (strings[strings.size - 1]).toLong()
|
recordingID = (strings[strings.size - 1]).toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEventString():String {
|
||||||
val split = eventUrl.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
val split = eventUrl.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
eventID = (split[split.size - 1]).toLong()
|
return (split[split.size - 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ interface EventDao: PersistentItemDao<PersistentEvent> {
|
||||||
@Query("SELECT * FROM event WHERE conference = :id ORDER BY title ASC")
|
@Query("SELECT * FROM event WHERE conference = :id ORDER BY title ASC")
|
||||||
fun findEventsByConference(id: Long):LiveData<List<PersistentEvent>>
|
fun findEventsByConference(id: Long):LiveData<List<PersistentEvent>>
|
||||||
|
|
||||||
@Query("SELECT * FROM event WHERE conference = :id ORDER BY title ASC")
|
@Query("SELECT * FROM event WHERE conferenceId = :id ORDER BY title ASC")
|
||||||
fun findEventsByConferenceSync(id: Long):List<PersistentEvent>
|
fun findEventsByConferenceSync(id: Long):List<PersistentEvent>
|
||||||
|
|
||||||
@Query("SELECT * FROM event INNER JOIN watchlist_item WHERE event.id = watchlist_item.event_id")
|
@Query("SELECT * FROM event INNER JOIN watchlist_item WHERE event.id = watchlist_item.event_id")
|
||||||
|
|
|
@ -13,7 +13,7 @@ import de.nicidienase.chaosflix.common.mediadata.entities.recording.Recording
|
||||||
childColumns = arrayOf("eventId"))),
|
childColumns = arrayOf("eventId"))),
|
||||||
indices = arrayOf(Index("eventId")))
|
indices = arrayOf(Index("eventId")))
|
||||||
data class PersistentRecording(
|
data class PersistentRecording(
|
||||||
var eventId: Long,
|
var eventId: Long = 0,
|
||||||
var size: Int = 0,
|
var size: Int = 0,
|
||||||
var length: Int = 0,
|
var length: Int = 0,
|
||||||
var mimeType: String = "",
|
var mimeType: String = "",
|
||||||
|
@ -51,23 +51,23 @@ data class PersistentRecording(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
constructor(rec: Recording) : this(
|
constructor(rec: Recording, eventId: Long = 0) : this(
|
||||||
rec.eventID,
|
eventId = eventId,
|
||||||
rec.size,
|
size = rec.size,
|
||||||
rec.length,
|
length = rec.length,
|
||||||
rec.mimeType,
|
mimeType = rec.mimeType,
|
||||||
rec.language,
|
language = rec.language,
|
||||||
rec.filename,
|
filename = rec.filename,
|
||||||
rec.state,
|
state = rec.state,
|
||||||
rec.folder,
|
folder = rec.folder,
|
||||||
rec.isHighQuality,
|
isHighQuality = rec.isHighQuality,
|
||||||
rec.width,
|
width = rec.width,
|
||||||
rec.height,
|
height = rec.height,
|
||||||
rec.updatedAt,
|
updatedAt = rec.updatedAt,
|
||||||
rec.recordingUrl,
|
recordingUrl = rec.recordingUrl,
|
||||||
rec.url,
|
url = rec.url,
|
||||||
rec.eventUrl,
|
eventUrl = rec.eventUrl,
|
||||||
rec.conferenceUrl)
|
conferenceUrl = rec.conferenceUrl)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
parcel.writeLong(eventId)
|
parcel.writeLong(eventId)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.arch.persistence.room.OnConflictStrategy
|
||||||
import android.arch.persistence.room.Query
|
import android.arch.persistence.room.Query
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface RecordingDao{
|
interface RecordingDao: PersistentItemDao<RecordingDao>{
|
||||||
|
|
||||||
@Query("SELECT * FROM recording")
|
@Query("SELECT * FROM recording")
|
||||||
fun getAllRecordings(): LiveData<List<PersistentRecording>>
|
fun getAllRecordings(): LiveData<List<PersistentRecording>>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package de.nicidienase.chaosflix.common.mediadata.network
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import de.nicidienase.chaosflix.R
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class ApiFactory(val res: Resources) {
|
||||||
|
|
||||||
|
val gsonConverterFactory by lazy { GsonConverterFactory.create(Gson()) }
|
||||||
|
|
||||||
|
val client: OkHttpClient by lazy {
|
||||||
|
OkHttpClient.Builder()
|
||||||
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val recordingApi: RecordingService by lazy {
|
||||||
|
Retrofit.Builder()
|
||||||
|
.baseUrl(res.getString(R.string.api_media_ccc_url))
|
||||||
|
.client(client)
|
||||||
|
.addConverterFactory(gsonConverterFactory)
|
||||||
|
.build()
|
||||||
|
.create(RecordingService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
val streamingApi: StreamingService by lazy { Retrofit.Builder()
|
||||||
|
.baseUrl(res.getString(R.string.streaming_media_ccc_url))
|
||||||
|
.client(client)
|
||||||
|
.addConverterFactory(gsonConverterFactory)
|
||||||
|
.build()
|
||||||
|
.create(StreamingService::class.java) }
|
||||||
|
}
|
|
@ -4,31 +4,28 @@ import de.nicidienase.chaosflix.common.mediadata.entities.recording.Conference
|
||||||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.ConferencesWrapper
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.ConferencesWrapper
|
||||||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Event
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Event
|
||||||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Recording
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Recording
|
||||||
import io.reactivex.Single
|
import retrofit2.Call
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
|
|
||||||
public interface RecordingService {
|
interface RecordingService {
|
||||||
|
|
||||||
@GET("public/conferences")
|
@GET("public/conferences")
|
||||||
fun getConferencesWrapper(): Single<ConferencesWrapper>
|
fun getConferencesWrapper(): Call<ConferencesWrapper>
|
||||||
|
|
||||||
@GET("public/events")
|
|
||||||
fun getAllEvents(): Single<List<Event>>
|
|
||||||
|
|
||||||
@GET("public/conferences/{id}")
|
@GET("public/conferences/{id}")
|
||||||
fun getConference(@Path("id") id: Long): Single<Conference>
|
fun getConference(@Path("id") id: Long): Call<Conference>
|
||||||
|
|
||||||
@GET("public/conferences/{id}")
|
|
||||||
fun getConferenceString(@Path("id") id: Long): Single<String>
|
|
||||||
|
|
||||||
@GET("public/conferences/{name}")
|
@GET("public/conferences/{name}")
|
||||||
fun getConferenceByname(@Path("name") name: String): Single<Conference>
|
fun getConferenceByName(@Path("name") name: String): Call<Conference>
|
||||||
|
|
||||||
@GET("public/events/{id}")
|
@GET("public/events/{id}")
|
||||||
fun getEvent(@Path("id") id: Long): Single<Event>
|
fun getEvent(@Path("id") id: Long): Call<Event>
|
||||||
|
|
||||||
|
@GET("public/events/{guid}")
|
||||||
|
fun getEventByGUID(@Path("guid") guid: String): Call<Event>
|
||||||
|
|
||||||
@GET("public/recordings/{id}")
|
@GET("public/recordings/{id}")
|
||||||
fun getRecording(@Path("id") id: Long): Single<Recording>
|
fun getRecording(@Path("id") id: Long): Call<Recording>
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
package de.nicidienase.chaosflix.common.mediadata.network
|
package de.nicidienase.chaosflix.common.mediadata.network
|
||||||
|
|
||||||
import de.nicidienase.chaosflix.common.mediadata.entities.streaming.LiveConference
|
import de.nicidienase.chaosflix.common.mediadata.entities.streaming.LiveConference
|
||||||
import io.reactivex.Flowable
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
|
|
||||||
public interface StreamingService {
|
interface StreamingService {
|
||||||
|
|
||||||
@GET("streams/v2.json")
|
@GET("streams/v2.json")
|
||||||
fun getStreamingConferences(): Single<List<LiveConference>>
|
fun getStreamingConferences(): Single<List<LiveConference>>
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package de.nicidienase.chaosflix.touch.sync
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.support.v4.app.JobIntentService
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.sync.Downloader
|
||||||
|
import de.nicidienase.chaosflix.touch.ViewModelFactory
|
||||||
|
|
||||||
|
class DownloadJobService : JobIntentService() {
|
||||||
|
|
||||||
|
override fun onHandleWork(intent: Intent) {
|
||||||
|
val downloader = Downloader(ViewModelFactory.recordingApi, ViewModelFactory.database)
|
||||||
|
val entity: String? = intent.getStringExtra(ENTITY_KEY)
|
||||||
|
val id: Long = intent.getLongExtra(ID_KEY, -1)
|
||||||
|
if (entity != null) {
|
||||||
|
when (entity) {
|
||||||
|
// ENTITY_KEY_EVERYTHING -> downloader.updateEverything()
|
||||||
|
ENTITY_KEY_CONFERENCES -> downloader.updateConferencesAndGroups()
|
||||||
|
ENTITY_KEY_EVENTS -> downloader.updateEventsForConference(id)
|
||||||
|
ENTITY_KEY_RECORDINGS -> downloader.updateRecordingsForEvent(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ENTITY_KEY: String = "entity_key"
|
||||||
|
// val ENTITY_KEY_EVERYTHING = "everything"
|
||||||
|
val ENTITY_KEY_CONFERENCES: String = "conferences"
|
||||||
|
val ENTITY_KEY_EVENTS: String = "events"
|
||||||
|
val ENTITY_KEY_RECORDINGS: String = "recodings"
|
||||||
|
val ID_KEY: String = "id_key"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package de.nicidienase.chaosflix.common.mediadata.sync
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData
|
||||||
|
import android.database.sqlite.SQLiteConstraintException
|
||||||
|
import android.util.Log
|
||||||
|
import de.nicidienase.chaosflix.common.Util
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.MediaDatabase
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.ConferencesWrapper
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Event
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.Recording
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.ConferenceGroup
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.PersistentConference
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.PersistentEvent
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.PersistentRecording
|
||||||
|
import de.nicidienase.chaosflix.common.mediadata.network.RecordingService
|
||||||
|
import de.nicidienase.chaosflix.common.util.LiveEvent
|
||||||
|
import de.nicidienase.chaosflix.common.util.SingleLiveEvent
|
||||||
|
import de.nicidienase.chaosflix.common.util.ThreadHandler
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
|
class Downloader(val recordingApi: RecordingService,
|
||||||
|
val database: MediaDatabase) {
|
||||||
|
|
||||||
|
private val threadHandler = ThreadHandler()
|
||||||
|
|
||||||
|
enum class DownloaderState{
|
||||||
|
RUNNING, DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateConferencesAndGroups(): LiveData<LiveEvent<DownloaderState, ConferencesWrapper, String>> {
|
||||||
|
val updateState = SingleLiveEvent<LiveEvent<DownloaderState, ConferencesWrapper, String>>()
|
||||||
|
threadHandler.runOnBackgroundThread {
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.RUNNING,null, null))
|
||||||
|
val response = recordingApi.getConferencesWrapper().execute()
|
||||||
|
|
||||||
|
if(!response.isSuccessful){
|
||||||
|
updateState.postValue(LiveEvent(state = DownloaderState.DONE, error = response.message()))
|
||||||
|
return@runOnBackgroundThread
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
response.body()?.let { conferencesWrapper ->
|
||||||
|
saveConferences(conferencesWrapper)
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE,conferencesWrapper))
|
||||||
|
}
|
||||||
|
} catch (e: Exception){
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, error = "Error updating conferences."))
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updateState
|
||||||
|
}
|
||||||
|
|
||||||
|
private val TAG: String? = Downloader::class.simpleName
|
||||||
|
|
||||||
|
fun updateEventsForConference(conference: PersistentConference) : LiveData<LiveEvent<DownloaderState, List<PersistentEvent>, String>> {
|
||||||
|
val updateState = SingleLiveEvent<LiveEvent<DownloaderState, List<PersistentEvent>, String>>()
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.RUNNING))
|
||||||
|
threadHandler.runOnBackgroundThread {
|
||||||
|
val response = recordingApi.getConferenceByName(conference.acronym).execute()
|
||||||
|
if(!response.isSuccessful){
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE,error = response.message()))
|
||||||
|
return@runOnBackgroundThread
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val persistentEvents = response.body()?.events?.let { events ->
|
||||||
|
return@let saveEvents(conference, events)
|
||||||
|
}
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, data = persistentEvents))
|
||||||
|
} catch (e: Exception){
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, error = "Error updating Events for ${conference.acronym}"))
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updateState
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRecordingsForEvent(event: PersistentEvent) :
|
||||||
|
LiveData<LiveEvent<DownloaderState, List<PersistentRecording>, String>> {
|
||||||
|
val updateState = SingleLiveEvent<LiveEvent<DownloaderState, List<PersistentRecording>, String>>()
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.RUNNING))
|
||||||
|
threadHandler.runOnBackgroundThread {
|
||||||
|
val response = recordingApi.getEventByGUID(event.guid).execute()
|
||||||
|
if(!response.isSuccessful){
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, error = response.message()))
|
||||||
|
return@runOnBackgroundThread
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val recordings = response.body()?.recordings?.let { recordings ->
|
||||||
|
return@let saveRecordings(event, recordings)
|
||||||
|
}
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, data = recordings))
|
||||||
|
} catch (e: Exception){
|
||||||
|
updateState.postValue(LiveEvent(DownloaderState.DONE, error = "Error updating Recordings for ${event.title}"))
|
||||||
|
e.printStackTrace()}
|
||||||
|
}
|
||||||
|
return updateState
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveConferences(conferencesWrapper: ConferencesWrapper): List<PersistentConference> {
|
||||||
|
return conferencesWrapper.conferencesMap.map { entry ->
|
||||||
|
val conferenceGroup: ConferenceGroup = getOrCreateConferenceGroup(entry.key)
|
||||||
|
val conferenceList = entry.value
|
||||||
|
.map { PersistentConference(it) }
|
||||||
|
.map { it.conferenceGroupId = conferenceGroup.id; it }.toTypedArray()
|
||||||
|
val conferenceIds = database.conferenceDao().insert(*conferenceList).toList()
|
||||||
|
return@map conferenceList.zip(conferenceIds) { conf: PersistentConference, id: Long ->
|
||||||
|
conf.id = id
|
||||||
|
return@zip conf
|
||||||
|
}
|
||||||
|
}.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrCreateConferenceGroup(name: String): ConferenceGroup{
|
||||||
|
val conferenceGroup: ConferenceGroup?
|
||||||
|
= database.conferenceGroupDao().getConferenceGroupByName(name)
|
||||||
|
if (conferenceGroup != null) {
|
||||||
|
return conferenceGroup
|
||||||
|
}
|
||||||
|
val group = ConferenceGroup(name)
|
||||||
|
val index = Util.orderedConferencesList.indexOf(group.name)
|
||||||
|
if (index != -1)
|
||||||
|
group.index = index
|
||||||
|
else if (group.name == "other conferences")
|
||||||
|
group.index = 1_000_001
|
||||||
|
group.id = database.conferenceGroupDao().insert(group)
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveEvents(persistentConference: PersistentConference, events: List<Event>): List<PersistentEvent> {
|
||||||
|
val persistantEvents = events.map { PersistentEvent(it,persistentConference.id) }
|
||||||
|
val insertEventIds = database.eventDao().insert(*(persistantEvents.toTypedArray())).asList()
|
||||||
|
val oldEvents = database.eventDao()
|
||||||
|
.findEventsByConferenceSync(persistentConference.id)
|
||||||
|
.filter { !insertEventIds.contains(it.id) }
|
||||||
|
.toTypedArray()
|
||||||
|
try {
|
||||||
|
database.eventDao().delete(*oldEvents)
|
||||||
|
} catch (ex: SQLiteConstraintException){
|
||||||
|
Log.d(TAG,"SQLiteException",ex)
|
||||||
|
}
|
||||||
|
persistantEvents.zip(insertEventIds) {event: PersistentEvent, id: Long ->
|
||||||
|
event.id = id
|
||||||
|
}
|
||||||
|
return persistantEvents
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveRecordings(event: PersistentEvent,recordings: List<Recording>): List<PersistentRecording> {
|
||||||
|
val persistentRecordings = recordings.map { PersistentRecording(it, event.id) }
|
||||||
|
database.recordingDao().insert(*persistentRecordings.toTypedArray())
|
||||||
|
return persistentRecordings
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package de.nicidienase.chaosflix.common.util
|
||||||
|
|
||||||
|
class LiveEvent<T,U,V>(
|
||||||
|
val state: T,
|
||||||
|
val data: U? = null,
|
||||||
|
val error: V? = null)
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.common.util
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LifecycleOwner
|
||||||
|
import android.arch.lifecycle.MutableLiveData
|
||||||
|
import android.arch.lifecycle.Observer
|
||||||
|
import android.support.annotation.MainThread
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
|
||||||
|
* navigation and Snackbar messages.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This avoids a common problem with events: on configuration change (like rotation) an update
|
||||||
|
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
|
||||||
|
* explicit call to setValue() or call().
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Note that only one observer is going to be notified of changes.
|
||||||
|
*/
|
||||||
|
class SingleLiveEvent<T> : MutableLiveData<T>() {
|
||||||
|
|
||||||
|
private val mPending = AtomicBoolean(false)
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
|
||||||
|
|
||||||
|
if (hasActiveObservers()) {
|
||||||
|
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe the internal MutableLiveData
|
||||||
|
super.observe(owner, Observer { t ->
|
||||||
|
if (mPending.compareAndSet(true, false)) {
|
||||||
|
observer.onChanged(t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
override fun setValue(t: T?) {
|
||||||
|
mPending.set(true)
|
||||||
|
super.setValue(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for cases where T is Void, to make calls cleaner.
|
||||||
|
*/
|
||||||
|
@MainThread
|
||||||
|
fun call() {
|
||||||
|
value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val TAG = "SingleLiveEvent"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package de.nicidienase.chaosflix.common.util
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Process
|
||||||
|
|
||||||
|
class ThreadHandler {
|
||||||
|
|
||||||
|
private val uiThreadHandler: Handler
|
||||||
|
private val backgroundThreadHandler: Handler
|
||||||
|
|
||||||
|
init {
|
||||||
|
val handlerTread = HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND)
|
||||||
|
handlerTread.start()
|
||||||
|
backgroundThreadHandler = android.os.Handler(handlerTread.looper)
|
||||||
|
uiThreadHandler = Handler(Looper.getMainLooper())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runOnBackgroundThread(runnable: ()->Unit){
|
||||||
|
backgroundThreadHandler.post(runnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runOnMainThread(runnable: ()->Unit){
|
||||||
|
uiThreadHandler.post(runnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = ThreadHandler::class.java.simpleName
|
||||||
|
}
|
||||||
|
}
|
5
common/src/main/res/values/urlstrings.xml
Normal file
5
common/src/main/res/values/urlstrings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="recording_url">https://api.media.ccc.de</string>
|
||||||
|
<string name="streaming_url">https://streaming.media.ccc.de</string>
|
||||||
|
</resources>
|
Loading…
Add table
Reference in a new issue