extracte module common

This commit is contained in:
Felix 2017-09-18 21:56:00 +02:00
parent 9d698f2923
commit d0b2a4d6c3
101 changed files with 80 additions and 5465 deletions

View file

@ -1,91 +0,0 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "de.nicidienase.chaosflix"
minSdkVersion 22
targetSdkVersion 25
versionCode 5
versionName "0.2.0"
multiDexEnabled true
}
buildTypes {
debug {
minifyEnabled false
useProguard false
}
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/notice.txt'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'com.android.support:support-v4:25.4.0'
implementation 'com.android.support:recyclerview-v7:25.4.0'
implementation 'com.android.support:cardview-v7:25.4.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:leanback-v17:25.4.0'
compile 'com.android.support:appcompat-v7:25.4.0'
compile 'com.android.support:multidex:1.0.2'
compile 'com.android.support:design:25.4.0'
compile 'com.google.android.exoplayer:exoplayer:r2.5.2'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'joda-time:joda-time:2.9.9'
compile 'org.joda:joda-convert:1.8'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
compile 'com.github.satyan:sugar:1.4'
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
debugCompile 'com.facebook.stetho:stetho:1.4.2'
debugCompile 'com.facebook.stetho:stetho-okhttp:1.4.2'
debugCompile 'com.facebook.stetho:stetho-okhttp3:1.4.2'
androidTestCompile('com.android.support.test:rules:0.5') {
exclude module: 'support-annotations'
}
androidTestCompile('com.android.support.test:runner:0.5') {
exclude module: 'support-annotations'
}
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.6.0'
//androidTestCompile group: 'commons-io', name: 'commons-io', version: '2.4'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
}
}

View file

@ -1,36 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /opt/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
-dontwarn javax.annotation.**
-dontwarn sun.misc.Unsafe
-dontwarn okio.**
-keep class de.nicidienase.chaosflix.shared.entities.** { *; }
#retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes *Annotation*,Signature, Exceptions
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
#endRetrofit

View file

@ -1,58 +0,0 @@
package de.nicidienase.chaosflix;
import android.content.Intent;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.nicidienase.chaosflix.test.R;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import de.nicidienase.chaosflix.leanback.activities.ConferencesActivity;
import okhttp3.mockwebserver.MockWebServer;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class ConferencesActivityTest {
@Rule
public ActivityTestRule<ConferencesActivity> mActivityTestRule
= new ActivityTestRule<>(ConferencesActivity.class, false, false);
private static MockWebServer server;
private UiDevice mDevice;
@Before
public void setup() throws IOException, TimeoutException {
mDevice = UiDevice.getInstance(getInstrumentation());
server = new MockWebServer();
server.start();
String serverUrl = server.url("").toString();
server.enqueue(TestHelper.getResponseForRaw(R.raw.conferences_json));
server.enqueue(TestHelper.getResponseForRaw(R.raw.conferences_101_33c3_json));
Intent i = new Intent();
i.putExtra("server_url",serverUrl);
mActivityTestRule.launchActivity(i);
}
@Test
public void conferencesActivityTest() throws InterruptedException {
mDevice.pressDPadRight();
mDevice.pressDPadRight();
mDevice.pressDPadCenter();
}
}

View file

@ -1,110 +0,0 @@
package de.nicidienase.chaosflix;
import android.content.Intent;
import android.os.IBinder;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ServiceTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.TimeoutException;
import de.nicidienase.chaosflix.test.R;
import de.nicidienase.chaosflix.shared.network.MediaApiService;
import okhttp3.mockwebserver.MockWebServer;
import static junit.framework.Assert.fail;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
/**
* Created by felix on 17.03.17.
*/
@RunWith(AndroidJUnit4.class)
public class MediaApiServiceTest {
@ClassRule
public static final ServiceTestRule mServiceRule = new ServiceTestRule();
private static final String TAG = MediaApiServiceTest.class.getSimpleName();
private static MockWebServer server;
private static String serverUrl;
private static MediaApiService service;
@BeforeClass
public static void setup() throws IOException, TimeoutException {
server = new MockWebServer();
server.start();
serverUrl = server.url("").toString();
Intent s = new Intent(InstrumentationRegistry.getTargetContext(),
MediaApiService.class);
s.putExtra(MediaApiService.RECORDING_URL,serverUrl);
s.putExtra(MediaApiService.STREAMING_URL,serverUrl);
IBinder binder = mServiceRule.bindService(s);
service = ((MediaApiService.LocalBinder) binder).getService();
}
@After
public void cleanup(){
}
@Test
public void getConferenceTest() throws IOException {
server.enqueue(TestHelper.getResponseForRaw(R.raw.conferences_101_33c3_json));
service.getConference(101)
.doOnError(throwable -> fail())
.blockingSubscribe
(conference -> assertThat(conference.getAcronym(),is("33c3")));
}
@Test
public void sortAllEvents() throws IOException {
server.enqueue(TestHelper.getResponseForRaw(R.raw.events_json));
service.getEvents()
.doOnError(throwable -> fail())
.blockingSubscribe(events -> Collections.sort(events));
}
@Test
public void getEventTest() throws IOException {
server.enqueue(TestHelper.getResponseForRaw(R.raw.events_2837_json));
service.getEvent(2837)
.doOnError(throwable -> fail())
.blockingSubscribe(
event -> assertThat(event.getGuid(), is("9f2e9ff0-1555-470b-8743-9f07f54e9097")));
}
@Test
public void testStreamingConferences() throws IOException {
server.enqueue(TestHelper.getResponseForRaw(R.raw.streams_v2_json));
service.getStreamingConferences()
.doOnError(throwable -> fail())
.blockingSubscribe(liveConferences -> assertThat(1,is(liveConferences.size())));
}
@Test
public void test2() throws IOException {
server.enqueue(TestHelper.getResponseForRaw(R.raw.streams_v2_json));
service.getStreamingConferences()
.doOnError(throwable -> fail())
.blockingSubscribe(liveConferences -> assertThat("FOSSGIS 2017",is(liveConferences.get(0).getConference())));
}
}

View file

@ -1,35 +0,0 @@
package de.nicidienase.chaosflix;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Arrays;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
/**
* Created by felix on 25.03.17.
*/
public class TestHelper {
static String getStringFromRaw(int resourceID) throws IOException {
InputStream inputStream = InstrumentationRegistry
.getContext().getResources().openRawResource(resourceID);
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer);
return writer.toString();
}
static MockResponse getResponseForRaw(int resourceID) throws IOException {
return new MockResponse().setBody(
getStringFromRaw(resourceID));
}
}

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"acronym":"mrmcd101b","aspect_ratio":"4:3","updated_at":"2015-12-30T12:39:19.125+01:00","title":"mrmcd101b","schedule_url":null,"slug":"conferences/mrmcd/mrmcd101b","webgen_location":"conferences/mrmcd/mrmcd101b","logo_url":"https://static.media.ccc.de/media/conferences/mrmcd/mrmcd101b/folder-mrmcd-2006.png","images_url":"https://static.media.ccc.de/media/conferences/mrmcd/mrmcd101b","recordings_url":"http://cdn.media.ccc.de/events/mrmcd/mrmcd101b","url":"https://api.media.ccc.de/public/conferences/13","events":[]}

View file

@ -1 +0,0 @@
{"acronym":"18c3","aspect_ratio":"4:3","updated_at":"2015-12-30T12:39:22.463+01:00","title":"18C3: Hacking Is Not A Crime","schedule_url":null,"slug":"congress/2001","webgen_location":"congress/2001","logo_url":"https://static.media.ccc.de/media/congress/2001/folder-18c3.png","images_url":"https://static.media.ccc.de/media/congress/2001","recordings_url":"http://cdn.media.ccc.de/congress/2001","url":"https://api.media.ccc.de/public/conferences/21","events":[{"guid":"import-86dcc5d427d763434f","title":"Final","subtitle":null,"slug":"18c3_final_divx","link":"http://ccc.de","description":"\n","original_language":"eng","persons":[],"tags":["18c3"],"date":"2001-01-01T01:00:00.000+01:00","release_date":"2008-01-01","updated_at":"2017-03-24T22:45:05.035+01:00","length":259,"thumb_url":"https://static.media.ccc.de/media/congress/2001/creative/18c3_final_divx.jpg","poster_url":"https://static.media.ccc.de/media/congress/2001/creative/18c3_final_divx_preview.jpg","frontend_link":"https://media.ccc.de/v/18c3_final_divx","url":"https://api.media.ccc.de/public/events/709","conference_url":"https://api.media.ccc.de/public/conferences/21"},{"guid":"import-996ee6767784dc2e88","title":"Blinkenlights ISDN","subtitle":null,"slug":"Blinkenlights_isdn","link":"http://ccc.de","description":"\n","original_language":"eng","persons":[],"tags":["blinkenlights"," 18c3"],"date":"2001-01-01T01:00:00.000+01:00","release_date":"2008-01-01","updated_at":"2017-03-24T11:45:06.628+01:00","length":4771,"thumb_url":"https://static.media.ccc.de/media/congress/2001/video/vortraege/tag2/saal2/Blinkenlights_isdn.jpg","poster_url":"https://static.media.ccc.de/media/congress/2001/video/vortraege/tag2/saal2/Blinkenlights_isdn_preview.jpg","frontend_link":"https://media.ccc.de/v/Blinkenlights_isdn","url":"https://api.media.ccc.de/public/events/710","conference_url":"https://api.media.ccc.de/public/conferences/21"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"guid":"f30baa0f-e541-4dc7-88cb-c6617efe659d","title":"From USA to DE.","subtitle":"A safe place for US hackers","slug":"gpn16-7677-from_usa_to_de","link":"https://entropia.de/GPN16:Fahrplan:XML?action=raw","description":"An overview of the possibilities US citisens have to live in Germany.\n","original_language":"eng","persons":["Sarah"],"tags":[],"date":"2016-05-26T20:00:00.000+02:00","release_date":"2016-05-27","updated_at":"2017-02-24T16:45:06.321+01:00","length":2267,"thumb_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7677-hd.jpg","poster_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7677-hd_preview.jpg","frontend_link":"https://media.ccc.de/v/gpn16-7677-from_usa_to_de","url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85","recordings":[{"size":28,"length":2261,"mime_type":"audio/opus","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE.opus","state":"new","folder":"opus","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-29T02:35:04.442+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/opus/gpn16-7677-eng-From_USA_to_DE.opus","url":"https://api.media.ccc.de/public/recordings/10684","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":231,"length":2267,"mime_type":"video/mp4","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE_hd.mp4","state":"new","folder":"h264-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-05-28T21:26:18.554+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-hd/gpn16-7677-eng-From_USA_to_DE_hd.mp4","url":"https://api.media.ccc.de/public/recordings/10618","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":34,"length":2261,"mime_type":"audio/mpeg","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE.mp3","state":"new","folder":"mp3","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-29T02:35:30.027+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/mp3/gpn16-7677-eng-From_USA_to_DE.mp3","url":"https://api.media.ccc.de/public/recordings/10685","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":78,"length":2267,"mime_type":"video/webm","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE_webm-sd.webm","state":"new","folder":"webm-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-05-29T02:36:04.124+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-sd/gpn16-7677-eng-From_USA_to_DE_webm-sd.webm","url":"https://api.media.ccc.de/public/recordings/10686","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":292,"length":2267,"mime_type":"video/webm","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE_webm-hd.webm","state":"new","folder":"webm-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-05-29T02:37:37.853+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-hd/gpn16-7677-eng-From_USA_to_DE_webm-hd.webm","url":"https://api.media.ccc.de/public/recordings/10688","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":74,"length":2267,"mime_type":"video/mp4","language":"eng","filename":"gpn16-7677-eng-From_USA_to_DE_sd.mp4","state":"new","folder":"h264-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-05-29T02:38:17.634+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-sd/gpn16-7677-eng-From_USA_to_DE_sd.mp4","url":"https://api.media.ccc.de/public/recordings/10689","event_url":"https://api.media.ccc.de/public/events/3062","conference_url":"https://api.media.ccc.de/public/conferences/85"}]}

View file

@ -1 +0,0 @@
{"guid":"7b59ca9e-7c40-4942-be90-2fd9acfea55b","title":"OSPAC","subtitle":"Open Source Podcast Audio Chain","slug":"gpn16-7675-ospac","link":"https://entropia.de/GPN16:Fahrplan:XML?action=raw","description":"Mit der Open-Source-Software Ospac kann man Mehrkanal-Aufnahmen für einen Podcast in veröffentlichbare Audio-Dateien abmischen. ","original_language":"deu","persons":["Sebastian Ritterbusch"],"tags":[],"date":"2016-05-26T21:30:00.000+02:00","release_date":"2016-05-27","updated_at":"2017-03-24T10:00:06.767+01:00","length":3306,"thumb_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7675-hd.jpg","poster_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7675-hd_preview.jpg","frontend_link":"https://media.ccc.de/v/gpn16-7675-ospac","url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85","recordings":[{"size":541,"length":3306,"mime_type":"video/mp4","language":"deu","filename":"gpn16-7675-deu-OSPAC_hd.mp4","state":"new","folder":"h264-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-05-27T02:28:51.224+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-hd/gpn16-7675-deu-OSPAC_hd.mp4","url":"https://api.media.ccc.de/public/recordings/10477","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":159,"length":3306,"mime_type":"video/webm","language":"deu","filename":"gpn16-7675-deu-OSPAC_webm-sd.webm","state":"new","folder":"webm-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-05-27T14:17:36.490+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-sd/gpn16-7675-deu-OSPAC_webm-sd.webm","url":"https://api.media.ccc.de/public/recordings/10486","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":50,"length":3300,"mime_type":"audio/mpeg","language":"deu","filename":"gpn16-7675-deu-OSPAC.mp3","state":"new","folder":"mp3","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-27T14:18:08.501+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/mp3/gpn16-7675-deu-OSPAC.mp3","url":"https://api.media.ccc.de/public/recordings/10487","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":42,"length":3300,"mime_type":"audio/opus","language":"deu","filename":"gpn16-7675-deu-OSPAC.opus","state":"new","folder":"opus","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-27T14:14:24.605+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/opus/gpn16-7675-deu-OSPAC.opus","url":"https://api.media.ccc.de/public/recordings/10484","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":145,"length":3306,"mime_type":"video/mp4","language":"deu","filename":"gpn16-7675-deu-OSPAC_sd.mp4","state":"new","folder":"h264-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-05-27T14:16:07.606+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-sd/gpn16-7675-deu-OSPAC_sd.mp4","url":"https://api.media.ccc.de/public/recordings/10485","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":507,"length":3306,"mime_type":"video/webm","language":"deu","filename":"gpn16-7675-deu-OSPAC_webm-hd.webm","state":"new","folder":"webm-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-05-27T14:13:52.563+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-hd/gpn16-7675-deu-OSPAC_webm-hd.webm","url":"https://api.media.ccc.de/public/recordings/10483","event_url":"https://api.media.ccc.de/public/events/3066","conference_url":"https://api.media.ccc.de/public/conferences/85"}]}

View file

@ -1 +0,0 @@
{"guid":"b2bc162c-1745-4f45-ba56-766b4174095b","title":"Run your own fucking infrastructure","subtitle":"Why you should consider running your own shit","slug":"gpn16-7582-run_your_own_fucking_infrastructure","link":"https://entropia.de/GPN16:Fahrplan:XML?action=raw","description":null,"original_language":"deu","persons":["mc.fly"],"tags":[],"date":"2016-05-29T13:15:00.000+02:00","release_date":"2016-05-29","updated_at":"2017-03-24T14:45:04.540+01:00","length":3505,"thumb_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7582-hd.jpg","poster_url":"https://static.media.ccc.de/media/events/gpn/gpn16/7582-hd_preview.jpg","frontend_link":"https://media.ccc.de/v/gpn16-7582-run_your_own_fucking_infrastructure","url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85","recordings":[{"size":590,"length":3505,"mime_type":"video/mp4","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure_hd.mp4","state":"new","folder":"h264-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-05-29T16:19:48.040+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-hd/gpn16-7582-deu-Run_your_own_fucking_infrastructure_hd.mp4","url":"https://api.media.ccc.de/public/recordings/10726","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":144,"length":3505,"mime_type":"video/webm","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure_webm-sd.webm","state":"new","folder":"webm-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-06-02T20:28:37.637+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-sd/gpn16-7582-deu-Run_your_own_fucking_infrastructure_webm-sd.webm","url":"https://api.media.ccc.de/public/recordings/10736","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":537,"length":3505,"mime_type":"video/webm","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure_webm-hd.webm","state":"new","folder":"webm-hd","high_quality":true,"width":1920,"height":1080,"updated_at":"2016-06-02T20:33:18.238+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/webm-hd/gpn16-7582-deu-Run_your_own_fucking_infrastructure_webm-hd.webm","url":"https://api.media.ccc.de/public/recordings/10738","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":53,"length":3499,"mime_type":"audio/mpeg","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure.mp3","state":"new","folder":"mp3","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-29T16:26:01.895+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/mp3/gpn16-7582-deu-Run_your_own_fucking_infrastructure.mp3","url":"https://api.media.ccc.de/public/recordings/10728","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":136,"length":3505,"mime_type":"video/mp4","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure_sd.mp4","state":"new","folder":"h264-sd","high_quality":false,"width":720,"height":576,"updated_at":"2016-05-29T16:31:11.730+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/h264-sd/gpn16-7582-deu-Run_your_own_fucking_infrastructure_sd.mp4","url":"https://api.media.ccc.de/public/recordings/10730","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"},{"size":47,"length":3499,"mime_type":"audio/opus","language":"deu","filename":"gpn16-7582-deu-Run_your_own_fucking_infrastructure.opus","state":"new","folder":"opus","high_quality":false,"width":0,"height":0,"updated_at":"2016-05-29T16:26:33.792+02:00","recording_url":"http://cdn.media.ccc.de/events/gpn/gpn16/opus/gpn16-7582-deu-Run_your_own_fucking_infrastructure.opus","url":"https://api.media.ccc.de/public/recordings/10729","event_url":"https://api.media.ccc.de/public/events/3104","conference_url":"https://api.media.ccc.de/public/conferences/85"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"guid":"import-86dcc5d427d763434f","title":"Final","subtitle":null,"slug":"18c3_final_divx","link":"http://ccc.de","description":"\n","original_language":"eng","persons":[],"tags":["18c3"],"date":"2001-01-01T01:00:00.000+01:00","release_date":"2008-01-01","updated_at":"2017-03-24T22:45:05.035+01:00","length":259,"thumb_url":"https://static.media.ccc.de/media/congress/2001/creative/18c3_final_divx.jpg","poster_url":"https://static.media.ccc.de/media/congress/2001/creative/18c3_final_divx_preview.jpg","frontend_link":"https://media.ccc.de/v/18c3_final_divx","url":"https://api.media.ccc.de/public/events/709","conference_url":"https://api.media.ccc.de/public/conferences/21","recordings":[{"size":null,"length":259,"mime_type":"video/mp4","language":"eng","filename":"18c3_final_divx.mp4","state":"downloaded","folder":"mp4","high_quality":true,"width":360,"height":280,"updated_at":"2016-01-27T11:03:09.279+01:00","recording_url":"http://cdn.media.ccc.de/congress/2001/mp4/18c3_final_divx.mp4","url":"https://api.media.ccc.de/public/recordings/4207","event_url":"https://api.media.ccc.de/public/events/709","conference_url":"https://api.media.ccc.de/public/conferences/21"}]}

View file

@ -1 +0,0 @@
{"guid":"import-996ee6767784dc2e88","title":"Blinkenlights ISDN","subtitle":null,"slug":"Blinkenlights_isdn","link":"http://ccc.de","description":"\n","original_language":"eng","persons":[],"tags":["blinkenlights"," 18c3"],"date":"2001-01-01T01:00:00.000+01:00","release_date":"2008-01-01","updated_at":"2017-03-24T11:45:06.628+01:00","length":4771,"thumb_url":"https://static.media.ccc.de/media/congress/2001/video/vortraege/tag2/saal2/Blinkenlights_isdn.jpg","poster_url":"https://static.media.ccc.de/media/congress/2001/video/vortraege/tag2/saal2/Blinkenlights_isdn_preview.jpg","frontend_link":"https://media.ccc.de/v/Blinkenlights_isdn","url":"https://api.media.ccc.de/public/events/710","conference_url":"https://api.media.ccc.de/public/conferences/21","recordings":[{"size":null,"length":4771,"mime_type":"video/mp4","language":"eng","filename":"Blinkenlights_isdn.mp4","state":"downloaded","folder":"video/vortraege/tag2/saal2","high_quality":false,"width":192,"height":144,"updated_at":"2016-02-03T14:06:10.016+01:00","recording_url":"http://cdn.media.ccc.de/congress/2001/video/vortraege/tag2/saal2/Blinkenlights_isdn.mp4","url":"https://api.media.ccc.de/public/recordings/4303","event_url":"https://api.media.ccc.de/public/events/710","conference_url":"https://api.media.ccc.de/public/conferences/21"}]}

File diff suppressed because one or more lines are too long

View file

@ -1,948 +0,0 @@
[
{
"conference": "32C3",
"group": "Lecture Rooms",
"rooms": [
{
"slug": "hall1",
"schedulename": "Hall 1",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/s1.png",
"link": "https://streaming.media.ccc.de/32c3/hall1/",
"display": "Hall 1",
"streams": [
{
"slug": "hd-native",
"display": "Hall 1 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s1_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s1_native_hd.m3u8"
}
}
},
{
"slug": "hd-translated",
"display": "Hall 1 FullHD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s1_translated_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s1_translated_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Hall 1 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s1_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s1_native_sd.m3u8"
}
}
},
{
"slug": "sd-translated",
"display": "Hall 1 SD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s1_translated_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s1_translated_sd.m3u8"
}
}
},
{
"slug": "slides-native",
"display": "Hall 1 Slides",
"type": "slides",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s1_native_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s1_native_slides.m3u8"
}
}
},
{
"slug": "slides-translated",
"display": "Hall 1 Slides (Translation)",
"type": "slides",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s1_translated_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s1_translated_slides.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Hall 1 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s1_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s1_native.opus"
}
}
},
{
"slug": "audio-translated",
"display": "Hall 1 Audio (Translation)",
"type": "audio",
"isTranslated": true,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s1_translated.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s1_translated.opus"
}
}
}
]
},
{
"slug": "hall2",
"schedulename": "Hall 2",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/s2.png",
"link": "https://streaming.media.ccc.de/32c3/hall2/",
"display": "Hall 2",
"streams": [
{
"slug": "hd-native",
"display": "Hall 2 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s2_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s2_native_hd.m3u8"
}
}
},
{
"slug": "hd-translated",
"display": "Hall 2 FullHD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s2_translated_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s2_translated_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Hall 2 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s2_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s2_native_sd.m3u8"
}
}
},
{
"slug": "sd-translated",
"display": "Hall 2 SD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s2_translated_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s2_translated_sd.m3u8"
}
}
},
{
"slug": "slides-native",
"display": "Hall 2 Slides",
"type": "slides",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s2_native_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s2_native_slides.m3u8"
}
}
},
{
"slug": "slides-translated",
"display": "Hall 2 Slides (Translation)",
"type": "slides",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s2_translated_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s2_translated_slides.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Hall 2 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s2_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s2_native.opus"
}
}
},
{
"slug": "audio-translated",
"display": "Hall 2 Audio (Translation)",
"type": "audio",
"isTranslated": true,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s2_translated.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s2_translated.opus"
}
}
}
]
},
{
"slug": "hallg",
"schedulename": "Hall G",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/s3.png",
"link": "https://streaming.media.ccc.de/32c3/hallg/",
"display": "Hall G",
"streams": [
{
"slug": "hd-native",
"display": "Hall G FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s3_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s3_native_hd.m3u8"
}
}
},
{
"slug": "hd-translated",
"display": "Hall G FullHD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s3_translated_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s3_translated_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Hall G SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s3_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s3_native_sd.m3u8"
}
}
},
{
"slug": "sd-translated",
"display": "Hall G SD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s3_translated_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s3_translated_sd.m3u8"
}
}
},
{
"slug": "slides-native",
"display": "Hall G Slides",
"type": "slides",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s3_native_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s3_native_slides.m3u8"
}
}
},
{
"slug": "slides-translated",
"display": "Hall G Slides (Translation)",
"type": "slides",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s3_translated_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s3_translated_slides.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Hall G Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s3_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s3_native.opus"
}
}
},
{
"slug": "audio-translated",
"display": "Hall G Audio (Translation)",
"type": "audio",
"isTranslated": true,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s3_translated.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s3_translated.opus"
}
}
}
]
},
{
"slug": "hall6",
"schedulename": "Hall 6",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/s4.png",
"link": "https://streaming.media.ccc.de/32c3/hall6/",
"display": "Hall 6",
"streams": [
{
"slug": "hd-native",
"display": "Hall 6 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s4_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_hd.m3u8"
}
}
},
{
"slug": "hd-translated",
"display": "Hall 6 FullHD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s4_translated_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s4_translated_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Hall 6 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s4_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_sd.m3u8"
}
}
},
{
"slug": "sd-translated",
"display": "Hall 6 SD Video (Translation)",
"type": "video",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s4_translated_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_translated_sd.m3u8"
}
}
},
{
"slug": "slides-native",
"display": "Hall 6 Slides",
"type": "slides",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s4_native_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_slides.m3u8"
}
}
},
{
"slug": "slides-translated",
"display": "Hall 6 Slides (Translation)",
"type": "slides",
"isTranslated": true,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, XXX kBit/s",
"url": "http://cdn.c3voc.de/s4_translated_slides.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_translated_slides.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Hall 6 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.opus"
}
}
},
{
"slug": "audio-translated",
"display": "Hall 6 Audio (Translation)",
"type": "audio",
"isTranslated": true,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s4_translated.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s4_translated.opus"
}
}
}
]
}
]
},
{
"conference": "32C3",
"group": "Live Music",
"rooms": [
{
"slug": "lounge",
"schedulename": "lounge",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/lounge.png",
"link": "https://streaming.media.ccc.de/32c3/lounge/",
"display": "Lounge",
"streams": [
{
"slug": "music-native",
"display": "Lounge Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/lounge.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/lounge.opus"
}
}
}
]
},
{
"slug": "ambient",
"schedulename": "ambient",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/ambient.png",
"link": "https://streaming.media.ccc.de/32c3/ambient/",
"display": "Ambient",
"streams": [
{
"slug": "music-native",
"display": "Ambient Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/ambient.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/ambient.opus"
}
}
}
]
},
{
"slug": "dome",
"schedulename": "dome",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/dome.png",
"link": "https://streaming.media.ccc.de/32c3/dome/",
"display": "Dome",
"streams": [
{
"slug": "music-native",
"display": "Dome Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/dome.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/dome.opus"
}
}
}
]
},
{
"slug": "chaos-west",
"schedulename": "chaos-west",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/chaos-west.png",
"link": "https://streaming.media.ccc.de/32c3/chaos-west/",
"display": "Chaos-West",
"streams": [
{
"slug": "music-native",
"display": "Chaos-West Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/chaos-west.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/chaos-west.opus"
}
}
}
]
}
]
},
{
"conference": "32C3",
"group": "Live Podcasts",
"rooms": [
{
"slug": "sendezentrum",
"schedulename": "Bühne",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/s5.png",
"link": "https://streaming.media.ccc.de/32c3/sendezentrum/",
"display": "Sendezentrum",
"streams": [
{
"slug": "hd-stereo",
"display": "Sendezentrum FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s",
"url": "http://cdn.c3voc.de/s5_stereo_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s5_stereo_hd.m3u8"
}
}
},
{
"slug": "sd-stereo",
"display": "Sendezentrum SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 800 kBit/s",
"url": "http://cdn.c3voc.de/s5_stereo_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s5_stereo_sd.m3u8"
}
}
},
{
"slug": "audio-stereo",
"display": "Sendezentrum Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s5_stereo.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s5_stereo.opus"
}
}
}
]
},
{
"slug": "podcastertisch",
"schedulename": "Podcaster-Tisch",
"thumb": "https://streaming.media.ccc.de/32c3/../thumbs/podcastertisch.png",
"link": "https://streaming.media.ccc.de/32c3/podcastertisch/",
"display": "Sendezentrum - Podcastertisch",
"streams": [
{
"slug": "music-native",
"display": "Sendezentrum - Podcastertisch Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/podcastertisch.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/podcastertisch.opus"
}
}
}
]
}
]
}
]

View file

@ -1,186 +0,0 @@
[
{
"conference": "Easterhegg 2017",
"group": "Für die Glupscher",
"rooms": [
{
"slug": "vortragssaal",
"schedulename": "Vortragssaal",
"thumb": "http://streaming.media.ccc.de/thumbs/s6.png",
"link": "http://streaming.media.ccc.de/eh17/vortragssaal",
"display": "Vortragssaal",
"streams": [
{
"slug": "hd-native",
"display": "Vortragssaal FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "http://cdn.c3voc.de/s6_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s6_native_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Vortragssaal SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "http://cdn.c3voc.de/s6_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s6_native_sd.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Vortragssaal Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s6_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s6_native.opus"
}
}
}
]
},
{
"slug": "grosses-kolleg",
"schedulename": "Großes Kolleg",
"thumb": "http://streaming.media.ccc.de/thumbs/s4.png",
"link": "http://streaming.media.ccc.de/eh17/grosses-kolleg",
"display": "Großes Kolleg",
"streams": [
{
"slug": "hd-native",
"display": "Großes Kolleg FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "http://cdn.c3voc.de/s4_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Großes Kolleg SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "http://cdn.c3voc.de/s4_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_sd.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Großes Kolleg Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.opus"
}
}
}
]
}
]
},
{
"conference": "Easterhegg 2017",
"group": "Auf die Lauscher",
"rooms": [
{
"slug": "lounge",
"schedulename": "Lounge",
"thumb": "http://streaming.media.ccc.de/thumbs/lounge.png",
"link": "http://streaming.media.ccc.de/eh17/lounge",
"display": "Lounge",
"streams": [
{
"slug": "music-native",
"display": "Lounge Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/lounge.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/lounge.opus"
}
}
}
]
}
]
}
]

View file

@ -1,195 +0,0 @@
[
{
"conference": "Easterhegg 2017",
"slug": "eh17",
"author": "Easterhegg, you know!",
"description": "Ist das Zufall oder kann das weg?",
"keywords": "Easterhegg, Ostern, Hack, CCC, Zufall, Mühlheim, Video, Streaming, Live, Livestream",
"startsAt": "2017-04-14T11:30:00+0000",
"endsAt": "2017-04-17T14:30:00+0000",
"groups": [
{
"group": "Für die Glupscher",
"rooms": [
{
"slug": "vortragssaal",
"schedulename": "Vortragssaal",
"thumb": "http://streaming.media.ccc.de/thumbs/s6.png",
"link": "http://streaming.media.ccc.de/eh17/vortragssaal",
"display": "Vortragssaal",
"streams": [
{
"slug": "hd-native",
"display": "Vortragssaal FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "http://cdn.c3voc.de/s6_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s6_native_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Vortragssaal SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "http://cdn.c3voc.de/s6_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s6_native_sd.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Vortragssaal Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s6_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s6_native.opus"
}
}
}
]
},
{
"slug": "grosses-kolleg",
"schedulename": "Großes Kolleg",
"thumb": "http://streaming.media.ccc.de/thumbs/s4.png",
"link": "http://streaming.media.ccc.de/eh17/grosses-kolleg",
"display": "Großes Kolleg",
"streams": [
{
"slug": "hd-native",
"display": "Großes Kolleg FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "http://cdn.c3voc.de/s4_native_hd.webm"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_hd.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "Großes Kolleg SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "http://cdn.c3voc.de/s4_native_sd.webm"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "http://cdn.c3voc.de/hls/s4_native_sd.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "Großes Kolleg Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "http://cdn.c3voc.de/s4_native.opus"
}
}
}
]
}
]
},
{
"group": "Auf die Lauscher",
"rooms": [
{
"slug": "lounge",
"schedulename": "Lounge",
"thumb": "http://streaming.media.ccc.de/thumbs/lounge.png",
"link": "http://streaming.media.ccc.de/eh17/lounge",
"display": "Lounge",
"streams": [
{
"slug": "music-native",
"display": "Lounge Radio",
"type": "music",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 192 kBit/s",
"url": "http://cdn.c3voc.de/lounge.mp3"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 96 kBit/s",
"url": "http://cdn.c3voc.de/lounge.opus"
}
}
}
]
}
]
}
]
}
]

View file

@ -1,234 +0,0 @@
[
{
"conference": "FOSSGIS 2017",
"slug": "fossgis17",
"author": "FOSSGIS e.V.",
"description": "Die FOSSGIS-Konferenz ist im D-A-CH Raum die führende Konferenz für Freie und Open Source Software für Geoinformationssysteme OpenStreetMap.",
"keywords": "FOSSGIS, FOSSGIS-Konferenz, 2017, FOSSGIS-Konferenz 2017, Open Source, GIS, Konferenz, Geoinformatik, OpenStreetMap, Passau, Video, Streaming, Live, Livestream",
"startsAt": "2017-03-22T08:30:00+0000",
"endsAt": "2017-03-25T14:00:00+0000",
"groups": [
{
"group": "Lecture Rooms",
"rooms": [
{
"slug": "AM-HS-9",
"schedulename": "AM HS 9",
"thumb": "https://streaming.media.ccc.de/thumbs/s6.png",
"link": "https://streaming.media.ccc.de/fossgis17/AM-HS-9",
"display": "AM HS 9",
"streams": [
{
"slug": "hd-native",
"display": "AM HS 9 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "AM HS 9 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "AM HS 9 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
}
]
},
{
"slug": "IM-HS-11",
"schedulename": "IM HS 11",
"thumb": "https://streaming.media.ccc.de/thumbs/s4.png",
"link": "https://streaming.media.ccc.de/fossgis17/IM-HS-11",
"display": "IM-HS-11",
"streams": [
{
"slug": "hd-native",
"display": "IM-HS-11 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "IM-HS-11 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "IM-HS-11 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
}
]
},
{
"slug": "IM-HS-13",
"schedulename": "IM HS 13",
"thumb": "https://streaming.media.ccc.de/thumbs/s5.png",
"link": "https://streaming.media.ccc.de/fossgis17/IM-HS-13",
"display": "IM HS 13",
"streams": [
{
"slug": "hd-native",
"display": "IM HS 13 FullHD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1920,
1080
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1920x1080, VP8+Vorbis in WebM, 3.5 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "sd-native",
"display": "IM HS 13 SD Video",
"type": "video",
"isTranslated": false,
"videoSize": [
1024,
576
],
"urls": {
"webm": {
"display": "WebM",
"tech": "1024x576, VP8+Vorbis in WebM, 1 MBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"hls": {
"display": "HLS",
"tech": "1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
},
{
"slug": "audio-native",
"display": "IM HS 13 Audio",
"type": "audio",
"isTranslated": false,
"videoSize": null,
"urls": {
"mp3": {
"display": "MP3",
"tech": "MP3-Audio, 96 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
"opus": {
"display": "Opus",
"tech": "Opus-Audio, 64 kBit/s",
"url": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}
}
}
]
}
]
}
]
}
]

View file

@ -1,31 +0,0 @@
package de.nicidienase.chaosflix;
import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;
import com.orm.SugarContext;
/**
* Created by felix on 18.03.17.
*/
public class ChaosflixApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SugarContext.init(this);
}
@Override
public void onTerminate() {
SugarContext.terminate();
super.onTerminate();
}
@Override
protected void attachBaseContext(Context base) {
MultiDex.install(base);
super.attachBaseContext(base);
}
}

View file

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="de.nicidienase.chaosflix"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature
android:name="android.software.leanback"
android:required="true"/>
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".ChaosflixApplication">
<activity
android:name=".leanback.activities.ConferencesActivity"
android:banner="@drawable/banner"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:logo="@drawable/icon"
android:theme="@style/Theme.Leanback"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".touch.activities.ConferencesActivity"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/Base.Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".leanback.activities.EventsActivity"
android:exported="true"
android:theme="@style/Theme.Leanback"/>
<activity android:name=".leanback.activities.EventDetailsActivity"
android:exported="true"
android:theme="@style/Theme.Leanback"/>
<activity android:name=".leanback.activities.DetailsActivity"
android:exported="true"
android:theme="@style/Theme.Leanback"/>
<activity android:name=".leanback.activities.PlayerActivity"
android:theme="@style/Theme.Leanback"/>
<service android:name=".shared.network.MediaApiService"/>
<meta-data android:name="DATABASE" android:value="mediaccc.db" />
<meta-data android:name="VERSION" android:value="4" />
<meta-data android:name="QUERY_LOG" android:value="false" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="de.nicidienase.chaosflix" />
</application>
</manifest>

View file

@ -1,138 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.leanback;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.Presenter;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.streaming.LiveConference;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
/*
* A CardPresenter is used to generate Views and bind Objects to them on demand.
* It contains an Image CardView
*/
public class CardPresenter extends Presenter {
private static final String TAG = "CardPresenter";
private static final int CARD_WIDTH = 313;
private static final int CARD_HEIGHT_4 = 235;
private static final int CARD_HEIGHT_16 = 177;
private static int sSelectedBackgroundColor;
private static int sDefaultBackgroundColor;
private Drawable mDefaultCardImage;
private static void updateCardBackgroundColor(ImageCardView view, boolean selected) {
int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor;
// Both background colors should be set because the view's background is temporarily visible
// during animations.
view.setBackgroundColor(color);
view.findViewById(R.id.info_field).setBackgroundColor(color);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
sDefaultBackgroundColor = parent.getResources().getColor(R.color.default_background);
sSelectedBackgroundColor = parent.getResources().getColor(R.color.selected_background);
mDefaultCardImage = parent.getResources().getDrawable(R.drawable.default_background);
ImageCardView cardView = new ImageCardView(parent.getContext()) {
@Override
public void setSelected(boolean selected) {
updateCardBackgroundColor(this, selected);
super.setSelected(selected);
}
};
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
updateCardBackgroundColor(cardView, false);
return new ViewHolder(cardView);
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
ImageCardView cardView = (ImageCardView) viewHolder.view;
cardView.setMainImageScaleType(ImageView.ScaleType.FIT_CENTER);
if (item instanceof Conference) {
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT_4);
Conference conference = (Conference) item;
cardView.setTitleText(conference.getTitle());
cardView.setContentText(conference.getAcronym());
if (conference.getLogoUrl() != null) {
Glide.with(viewHolder.view.getContext())
.load(conference.getLogoUrl())
.fitCenter()
.error(mDefaultCardImage)
.into(cardView.getMainImageView());
}
} else if (item instanceof Event) {
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT_16);
Event event = (Event) item;
cardView.setTitleText(event.getTitle());
cardView.setContentText(event.getSubtitle());
// cardView.setContentText(android.text.TextUtils.join(", ",event.getPersons()));
if (event.getThumbUrl() != null) {
Glide.with(viewHolder.view.getContext())
.load(event.getThumbUrl())
.fitCenter()
.error(mDefaultCardImage)
.into(cardView.getMainImageView());
}
} else if (item instanceof LiveConference) {
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT_4);
LiveConference con = (LiveConference) item;
cardView.setTitleText(con.getConference());
cardView.setMainImage(mDefaultCardImage);
cardView.setContentText(con.getDescription());
} else if (item instanceof Room) {
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT_16);
Room room = (Room) item;
cardView.setTitleText(room.getDisplay());
cardView.setContentText(room.getShedulename());
if (room.getThumb() != null) {
Glide.with(viewHolder.view.getContext())
.load(room.getThumb())
.fitCenter()
.error(mDefaultCardImage)
.into(cardView.getMainImageView());
}
} else if(item instanceof String) {
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT_4);
cardView.setTitleText((String) item);
Glide.with(viewHolder.view.getContext())
.load(R.drawable.icon)
.into(cardView.getMainImageView());
}
}
@Override
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
ImageCardView cardView = (ImageCardView) viewHolder.view;
// Remove references to images so that the garbage collector can free up memory
cardView.setBadgeImage(null);
cardView.setMainImage(null);
}
}

View file

@ -1,64 +0,0 @@
package de.nicidienase.chaosflix.leanback;
import android.content.Context;
import android.support.v17.leanback.widget.Presenter;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import butterknife.ButterKnife;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
/**
* Created by felix on 18.03.17.
*/
public class EventDetailsDescriptionPresenter extends Presenter {
private final Context mContext;
public EventDetailsDescriptionPresenter(Context context) {
mContext = context;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.detail_view_content, null);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder vh, Object item) {
TextView titleText = ButterKnife.findById(vh.view, R.id.title_text);
TextView speakersText = ButterKnife.findById(vh.view, R.id.speakers_text);
TextView subtitleText = ButterKnife.findById(vh.view, R.id.subtitle_text);
TextView descriptionText = ButterKnife.findById(vh.view, R.id.description_text);
if (item instanceof Event) {
Event event = (Event) item;
titleText.setText(event.getTitle());
subtitleText.setText(event.getSubtitle());
String speaker = TextUtils.join(", ", event.getPersons());
speakersText.setText(speaker);
StringBuilder sb = new StringBuilder();
sb.append(event.getDescription())
.append("\n")
.append("\nreleased at: ").append(event.getReleaseDate())
.append("\nTags: ").append(android.text.TextUtils.join(", ", event.getTags()));
descriptionText.setText(sb.toString());
} else if (item instanceof Room) {
Room room = (Room) item;
titleText.setText(room.getDisplay());
subtitleText.setText(room.getShedulename());
}
}
@Override
public void onUnbindViewHolder(ViewHolder viewHolder) {
}
}

View file

@ -1,57 +0,0 @@
package de.nicidienase.chaosflix.leanback;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.RowHeaderPresenter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import de.nicidienase.chaosflix.R;
/**
* Created by felix on 16.04.17.
*/
public class HeaderItemPresenter extends RowHeaderPresenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.header_item_layout, null);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
HeaderItem headerItem = ((ListRow) item).getHeaderItem();
View view = viewHolder.view;
ImageView headerIcon = (ImageView) view.findViewById(R.id.header_icon);
TextView headerLabel = (TextView) view.findViewById(R.id.header_label);
Resources resources = view.getContext().getResources();
String streamingPrefix = resources.getString(R.string.streaming_prefix);
if (headerItem.getName().startsWith(streamingPrefix)) {
Drawable camIcon = resources.getDrawable(R.drawable.ic_videocam_white_24dp);
headerIcon.setImageDrawable(camIcon);
((ListRow) item).setHeaderItem(new HeaderItem(headerItem.getName()
.substring(streamingPrefix.length())));
} else {
Drawable movieIcon = resources.getDrawable(R.drawable.ic_local_movies_white_24dp);
headerIcon.setImageDrawable(movieIcon);
headerLabel.setText(headerItem.getName());
}
}
@Override
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
}
}

View file

@ -1,70 +0,0 @@
package de.nicidienase.chaosflix.leanback;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v4.app.ActivityOptionsCompat;
import android.util.Log;
import de.nicidienase.chaosflix.leanback.activities.DetailsActivity;
import de.nicidienase.chaosflix.leanback.activities.EventDetailsActivity;
import de.nicidienase.chaosflix.leanback.activities.EventsActivity;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
/**
* Created by felix on 21.03.17.
*/
public class ItemViewClickedListener implements OnItemViewClickedListener {
private static final String TAG = ItemViewClickedListener.class.getSimpleName();
private Fragment fragment;
public ItemViewClickedListener(Fragment fragment) {
this.fragment = fragment;
}
@Override
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
Log.d(TAG, "onItemClicked");
if (item instanceof Conference) {
Conference conference = (Conference) item;
// Start EventsActivity for this conference
Intent i = new Intent(fragment.getActivity(), EventsActivity.class);
i.putExtra(EventsActivity.CONFERENCE, conference);
i.putExtra(EventsActivity.CONFERENCE_ID, conference.getApiID());
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
fragment.getActivity(),
((ImageCardView) itemViewHolder.view).getMainImageView(),
EventsActivity.SHARED_ELEMENT_NAME).toBundle();
fragment.startActivity(i, bundle);
} else if (item instanceof Event) {
Event event = (Event) item;
Intent i = new Intent(fragment.getActivity(), DetailsActivity.class);
i.putExtra(DetailsActivity.TYPE, DetailsActivity.TYPE_RECORDING);
i.putExtra(DetailsActivity.EVENT, event);
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
fragment.getActivity(),
((ImageCardView) itemViewHolder.view).getMainImageView(),
EventDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
fragment.getActivity().startActivity(i, bundle);
} else if (item instanceof Room) {
Room room = (Room) item;
Intent i = new Intent(fragment.getActivity(), DetailsActivity.class);
i.putExtra(DetailsActivity.TYPE, DetailsActivity.TYPE_STREAM);
i.putExtra(DetailsActivity.ROOM, room);
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
fragment.getActivity(),
((ImageCardView) itemViewHolder.view).getMainImageView(),
EventDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
fragment.getActivity().startActivity(i, bundle);
}
}
}

View file

@ -1,338 +0,0 @@
package de.nicidienase.chaosflix.leanback;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v17.leanback.app.PlaybackControlGlue;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
import android.util.Log;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.recording.Recording;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
import de.nicidienase.chaosflix.shared.entities.streaming.StreamUrl;
import de.nicidienase.chaosflix.leanback.fragments.OverlayFragment;
import io.reactivex.disposables.Disposable;
/**
* Created by felix on 26.03.17.
*/
public class PlaybackHelper extends PlaybackControlGlue {
private static final int[] SEEK_SPEEDS = {2, 4, 8, 16};
private static final int DEFAULT_UPDATE_PERIOD = 500;
private static final int UPDATE_PERIOD = 16;
private static final String TAG = PlaybackHelper.class.getSimpleName();
private Disposable thumbDisposable;
private OverlayFragment.PlaybackControlListener mControlListener;
private BitmapDrawable mDrawable = null;
private Room room;
private StreamUrl stream;
private OverlayFragment fragment;
private Event event;
private Recording recording;
private Runnable mUpdateProgressRunnable;
private Handler mHandler = new Handler();
PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
PlaybackControlsRow.FastForwardAction mFastForwardAction;
PlaybackControlsRow.RewindAction mRewindAction;
PlaybackControlsRow.SkipNextAction mSkipNextAction;
PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
private OverlayFragment.PlaybackControlListener mCallback;
private MediaController mMediaController;
private MediaController.TransportControls mTransportControls;
private ArrayObjectAdapter adapter;
public PlaybackHelper(Context context, OverlayFragment fragment, Event event, Recording recording) {
super(context, SEEK_SPEEDS);
mControlListener = (OverlayFragment.PlaybackControlListener) context;
this.fragment = fragment;
this.event = event;
this.recording = recording;
// thumbDisposable = Observable.fromCallable(() ->
// new BitmapDrawable(
// fragment.getResources(),
// Glide.with(getContext())
// .load(event.getThumbUrl())
// .asBitmap()
// .into(-1, -1)
// .get()))
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .doOnError(Throwable::printStackTrace)
// .subscribe(bitmapDrawable -> mDrawable = bitmapDrawable);
setup();
}
public PlaybackHelper(Context context, OverlayFragment fragment, Room room, StreamUrl stream) {
super(context, SEEK_SPEEDS);
mControlListener = (OverlayFragment.PlaybackControlListener) context;
this.fragment = fragment;
this.room = room;
this.stream = stream;
setup();
}
private void setup() {
mMediaController = fragment.getActivity().getMediaController();
if (mMediaController != null) {
mTransportControls = mMediaController.getTransportControls();
} else {
Log.d(TAG, "Could not get MediaController");
}
}
@Override
public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
adapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector());
getControlsRow().setSecondaryActionsAdapter(adapter);
mFastForwardAction = (PlaybackControlsRow.FastForwardAction) getPrimaryActionsAdapter()
.lookup(ACTION_FAST_FORWARD);
mRewindAction = (PlaybackControlsRow.RewindAction) getPrimaryActionsAdapter()
.lookup(ACTION_REWIND);
presenter.setOnActionClickedListener(new OnActionClickedListener() {
@Override
public void onActionClicked(Action action) {
dispatchAction(action);
}
});
return presenter;
}
@TargetApi(Build.VERSION_CODES.N)
public void dispatchAction(Action action) {
if (action instanceof PlaybackControlsRow.MultiAction) {
PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action;
multiAction.nextIndex();
notifyActionChanged(multiAction);
}
if (action == mFastForwardAction) {
mTransportControls.fastForward();
} else if (action == mRewindAction) {
mTransportControls.rewind();
} else {
super.onActionClicked(action);
}
}
private void notifyActionChanged(PlaybackControlsRow.MultiAction multiAction) {
int index;
index = getPrimaryActionsAdapter().indexOf(multiAction);
if (index >= 0) {
getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
} else {
index = getSecondaryActionsAdapter().indexOf(multiAction);
if (index >= 0) {
getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
}
}
}
@Override
public void enableProgressUpdating(boolean enable) {
mHandler.removeCallbacks(mUpdateProgressRunnable);
if (enable) {
mHandler.post(mUpdateProgressRunnable);
}
}
@Override
public void updateProgress() {
if (mUpdateProgressRunnable == null) {
mUpdateProgressRunnable = new Runnable() {
@Override
public void run() {
int totalTime = getControlsRow().getTotalTime();
long currentPosition = mControlListener.getCurrentPosition();
getControlsRow().setCurrentTimeLong(currentPosition);
long bufferedPosition = mControlListener.getBufferedPosition();
getControlsRow().setBufferedProgressLong(bufferedPosition);
if (totalTime > 0 && totalTime <= currentPosition) {
stopProgressAnimation();
} else {
updateProgress();
}
}
};
}
mHandler.postDelayed(mUpdateProgressRunnable, getUpdatePeriod());
}
@Override
public boolean hasValidMedia() {
return mediaIsRecording() || mediaIsStream();
}
@Override
public boolean isMediaPlaying() {
return mControlListener.isMediaPlaying();
}
@Override
public CharSequence getMediaTitle() {
if (mediaIsRecording()) {
return event.getTitle();
}
if (mediaIsStream()) {
return room.getDisplay();
}
return null;
}
@Override
public CharSequence getMediaSubtitle() {
if (mediaIsRecording()) {
return event.getSubtitle();
}
if (mediaIsStream()) {
return stream.getDisplay();
}
return null;
}
@Override
public int getMediaDuration() {
if (mediaIsRecording()) {
return event.getLength() * 1000;
}
return 0;
}
@Override
public Drawable getMediaArt() {
return mDrawable;
}
@Override
protected void startPlayback(int speed) {
if (getCurrentSpeedId() == speed) {
return;
}
if (mTransportControls != null) {
mTransportControls.play();
} else {
mControlListener.play();
}
}
@Override
protected void pausePlayback() {
if (mTransportControls != null) {
mTransportControls.pause();
} else {
mControlListener.pause();
}
}
@Override
protected void skipToNext() {
mTransportControls.skipToNext();
}
@Override
protected void skipToPrevious() {
mTransportControls.skipToPrevious();
}
private void stopProgressAnimation() {
if (mHandler != null && mUpdateProgressRunnable != null) {
mHandler.removeCallbacks(mUpdateProgressRunnable);
mUpdateProgressRunnable = null;
}
}
@Override
public long getSupportedActions() {
return ACTION_PLAY_PAUSE | ACTION_FAST_FORWARD | ACTION_REWIND | ACTION_SKIP_TO_PREVIOUS |
ACTION_SKIP_TO_NEXT;
}
@Override
public int getCurrentSpeedId() {
return isMediaPlaying() ? PLAYBACK_SPEED_NORMAL : PLAYBACK_SPEED_PAUSED;
}
@Override
public int getCurrentPosition() {
if (mControlListener != null) {
return (int) mControlListener.getCurrentPosition();
}
return 0;
}
public long getCurrentPositionLong() {
if (mControlListener != null) {
return mControlListener.getCurrentPosition();
}
return 0;
}
private boolean mediaIsStream() {
return (room != null && stream != null);
}
private boolean mediaIsRecording() {
return (event != null && recording != null);
}
public MediaController.Callback createMediaControllerCallback() {
return new ChaosflixMediaControllerCallback();
}
private SparseArrayObjectAdapter getPrimaryActionsAdapter() {
return (SparseArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
}
private ArrayObjectAdapter getSecondaryActionsAdapter() {
return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter();
}
private class ChaosflixMediaControllerCallback extends MediaController.Callback {
@Override
public void onPlaybackStateChanged(@NonNull PlaybackState state) {
if (state.getState() != PlaybackState.STATE_NONE) {
updateProgress();
}
onStateChanged();
}
@Override
public void onMetadataChanged(@Nullable MediaMetadata metadata) {
PlaybackHelper.this.onMetadataChanged();
PlaybackHelper.this.adapter.notifyArrayItemRangeChanged(0, 1);
}
}
public void onStop() {
if (thumbDisposable != null) {
thumbDisposable.dispose();
}
}
}

View file

@ -1,19 +0,0 @@
package de.nicidienase.chaosflix.leanback.activities;
import android.os.Bundle;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
/**
* Created by felix on 18.03.17.
*/
public class ConferencesActivity extends ChaosflixBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_conferences_grid);
setContentView(R.layout.activity_conferences_browse);
}
}

View file

@ -1,46 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.leanback.activities;
import android.os.Bundle;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
/*
* Details activity class that loads LeanbackDetailsFragment class
*/
public class DetailsActivity extends ChaosflixBaseActivity {
public static final String SHARED_ELEMENT_NAME = "hero";
public static final String EVENT = "event";
public static final String ROOM = "room";
public static final String STREAM_URL = "stream_url";
public static final String RECORDING = "recording";
public static final String TYPE = "event_type";
public static final int TYPE_RECORDING = 0;
public static final int TYPE_STREAM = 1;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_details);
}
}

View file

@ -1,23 +0,0 @@
package de.nicidienase.chaosflix.leanback.activities;
import android.os.Bundle;
import android.os.PersistableBundle;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
/**
* Created by felix on 18.03.17.
*/
public class EventDetailsActivity extends ChaosflixBaseActivity {
public static final String EVENT = "event";
public static final String SHARED_ELEMENT_NAME = "transision_element";
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_event_details);
}
}

View file

@ -1,20 +0,0 @@
package de.nicidienase.chaosflix.leanback.activities;
import android.os.Bundle;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
public class EventsActivity extends ChaosflixBaseActivity {
public static final String CONFERENCE_ID = "conference_id";
public static final String CONFERENCE = "conference";
public static final String SHARED_ELEMENT_NAME = "shared_element";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO determin if we should use a Browse or a Grid layout
setContentView(R.layout.activity_events_browse);
}
}

View file

@ -1,327 +0,0 @@
package de.nicidienase.chaosflix.leanback.activities;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.leanback.fragments.OverlayFragment;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
/**
* Created by felix on 26.03.17.
*/
public class PlayerActivity extends ChaosflixBaseActivity
implements OverlayFragment.PlaybackControlListener {
private static final String TAG = PlayerActivity.class.getSimpleName();
@BindView(R.id.videoView)
SurfaceView mSurfaceView;
@BindView(R.id.loading_player)
ProgressBar loadingSpinner;
OverlayFragment mPlaybackControllFragment;
private DefaultBandwidthMeter bandwidthMeter;
private SimpleExoPlayer player;
private String mUserAgent;
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private Handler mainHandler;
private DefaultTrackSelector trackSelector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playback);
ButterKnife.bind(this);
mSurfaceView.setKeepScreenOn(true);
mPlaybackControllFragment = (OverlayFragment) getFragmentManager().findFragmentById(R.id.playback_controls_fragment);
mUserAgent = Util.getUserAgent(this, getResources().getString(R.string.app_name));
synchronized (this) {
if (player == null) {
setupPlayer();
}
}
}
@Override
protected void onStart() {
super.onStart();
showLoadingSpinner();
// TODO get persisted playback progress
}
@Override
protected void onPause() {
super.onPause();
pause();
// TODO persist playback progress
}
private void setupPlayer() {
mUserAgent = Util.getUserAgent(this, getResources().getString(R.string.app_name));
mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory
= new AdaptiveTrackSelection.Factory(bandwidthMeter);
trackSelector
= new DefaultTrackSelector(videoTrackSelectionFactory);
LoadControl loadControl = new DefaultLoadControl();
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
player.setVideoSurfaceView(mSurfaceView);
player.setVideoListener(new SimpleExoPlayer.VideoListener() {
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
}
@Override
public void onRenderedFirstFrame() {
hideLoadingSpinner();
}
});
player.addListener(new ExoPlayer.EventListener() {
@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
@Override
public void onLoadingChanged(boolean isLoading) {
if(isLoading && player.getPlaybackState() != ExoPlayer.STATE_READY){
showLoadingSpinner();
} else{
hideLoadingSpinner();
}
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState){
case ExoPlayer.STATE_BUFFERING:
if(player.isLoading()){
showLoadingSpinner();
}
break;
case ExoPlayer.STATE_ENDED:
Log.d(TAG,"Finished Playback");
finish();
break;
case ExoPlayer.STATE_IDLE:
case ExoPlayer.STATE_READY:
default:
hideLoadingSpinner();
}
}
@Override
public void onRepeatModeChanged(int repeatMode) {
}
@Override
public void onPlayerError(ExoPlaybackException error) {
String errorMessage = error.getCause().getMessage();
Toast.makeText(PlayerActivity.this,errorMessage,Toast.LENGTH_SHORT).show();
Log.d(TAG,errorMessage,error);
PlayerActivity.this.finish();
}
@Override
public void onPositionDiscontinuity() {
}
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
});
}
private void showLoadingSpinner() {
loadingSpinner.setVisibility(View.VISIBLE);
}
private void hideLoadingSpinner(){
loadingSpinner.setVisibility(View.INVISIBLE);
}
@Override
public void setVideoSource(String source) {
Log.d(TAG, "Source: " + source);
synchronized (this) {
if (player == null) {
setupPlayer();
}
}
MediaSource mediaSource = buildMediaSource(Uri.parse(source), "");
player.prepare(mediaSource);
}
@Override
public void play() {
player.setPlayWhenReady(true);
}
@Override
public void pause() {
player.setPlayWhenReady(false);
}
@Override
public void playPause() {
player.setPlayWhenReady(!player.getPlayWhenReady());
}
@Override
public void seekTo(long sec) {
player.seekTo(sec * 1000);
}
@Override
public boolean isMediaPlaying() {
if (player != null) {
return player.getPlayWhenReady();
} else {
return false;
}
}
@Override
public long getCurrentPosition() {
if (player != null) {
return player.getCurrentPosition();
} else {
return 0;
}
}
@Override
public void releasePlayer() {
if (player != null) {
player.release();
}
}
@Override
public long getBufferedPosition() {
if (player != null) {
return player.getBufferedPosition();
}
return 0;
}
@Override
public void mute(boolean state) {
if (player != null) {
player.setVolume(state ? 0.0f : 1.0f);
}
}
@Override
public long getLength() {
if (player != null) {
return player.getDuration();
}
return 0;
}
@Override
public void nextAudioStream() {
// TODO cycle through audio streams
}
@Override
public void skipForward(int sec) {
player.seekTo(player.getCurrentPosition() + (sec * 1000));
}
@Override
public void skipBackward(int sec) {
player.seekTo(player.getCurrentPosition() - (sec * 1000));
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
DataSource.Factory mediaDataSourceFactory = buildDataSourceFactory(true);
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri)
: Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return new SsMediaSource(uri, buildDataSourceFactory(false),
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_DASH:
return new DashMediaSource(uri, buildDataSourceFactory(false),
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_HLS:
return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null);
case C.TYPE_OTHER:
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
mainHandler, null);
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
return buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
}
private DataSource.Factory buildDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
return new DefaultDataSourceFactory(this, bandwidthMeter,
buildHttpDataSourceFactory(bandwidthMeter));
}
private HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
return new DefaultHttpDataSourceFactory(mUserAgent, bandwidthMeter);
}
}

View file

@ -1,95 +0,0 @@
package de.nicidienase.chaosflix.leanback.fragments;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v17.leanback.app.ErrorFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import de.nicidienase.chaosflix.R;
/**
* Created by felix on 22.03.17.
*/
public class BrowseErrorFragment extends ErrorFragment {
private static final boolean TRANSLUCENT = true;
public static final String FRAGMENT = "fragmentId";
private SpinnerFragment mSpinnerFragment;
public static BrowseErrorFragment showErrorFragment(FragmentManager manager, int fragmentId) {
BrowseErrorFragment errorFragment = new BrowseErrorFragment();
Bundle args = new Bundle();
args.putInt(BrowseErrorFragment.FRAGMENT, fragmentId);
errorFragment.setArguments(args);
manager.beginTransaction().replace(fragmentId, errorFragment)
.addToBackStack(null).commit();
return errorFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int fragmentId = getArguments().getInt(FRAGMENT);
mSpinnerFragment = new SpinnerFragment();
getFragmentManager().beginTransaction().add(fragmentId, mSpinnerFragment).commit();
}
@Override
public void onStop() {
super.onStop();
getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit();
}
public void setErrorContent(int resourceId) {
setErrorContent(getResources().getString(resourceId));
}
public void setErrorContent(String message) {
setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud, null));
setMessage(message);
setDefaultBackground(TRANSLUCENT);
setButtonText(getResources().getString(R.string.dismiss_error));
setButtonClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
dismiss();
}
});
}
public void dismiss() {
FragmentManager fragmentManager = getFragmentManager();
if (fragmentManager != null) {
fragmentManager.beginTransaction().remove(BrowseErrorFragment.this).commit();
fragmentManager.popBackStack();
}
}
public static class SpinnerFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ProgressBar progressBar = new ProgressBar(container.getContext());
if (container instanceof FrameLayout) {
Resources res = getResources();
int width = res.getDimensionPixelSize(R.dimen.spinner_width);
int height = res.getDimensionPixelSize(R.dimen.spinner_height);
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(width, height, Gravity.CENTER);
progressBar.setLayoutParams(layoutParams);
}
return progressBar;
}
}
}

View file

@ -1,218 +0,0 @@
package de.nicidienase.chaosflix.leanback.fragments;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.SectionRow;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.nicidienase.chaosflix.BuildConfig;
import de.nicidienase.chaosflix.leanback.CardPresenter;
import de.nicidienase.chaosflix.leanback.ItemViewClickedListener;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.shared.entities.WatchlistItem;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.ConferencesWrapper;
import de.nicidienase.chaosflix.shared.entities.streaming.Group;
import de.nicidienase.chaosflix.shared.entities.streaming.LiveConference;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* Created by felix on 21.03.17.
*/
public class ConferencesBrowseFragment extends BrowseFragment {
private static final String TAG = ConferencesBrowseFragment.class.getSimpleName();
public static final int FRAGMENT = R.id.browse_fragment;
private ArrayObjectAdapter mRowsAdapter;
private Map<String, List<Conference>> mConferences;
CompositeDisposable mDisposables = new CompositeDisposable();
private ArrayObjectAdapter watchListAdapter;
private ListRow mWatchlistRow;
private boolean streamsAvailable = false;
private SectionRow mStreamingSection;
private SectionRow mRecomendationsSectionsRow;
private SectionRow mConferencesSection;
private boolean mWatchlistVisible = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(getResources().getString(R.string.app_name));
// setHeaderPresenterSelector(new PresenterSelector() {
// @Override
// public Presenter getPresenter(Object item) {
// return new HeaderItemPresenter();
// }
// });
final BrowseErrorFragment errorFragment =
BrowseErrorFragment.showErrorFragment(getFragmentManager(), FRAGMENT);
CardPresenter cardPresenter = new CardPresenter();
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
watchListAdapter = new ArrayObjectAdapter(cardPresenter);
Disposable disposable = ((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.subscribe(mediaApiService -> {
mDisposables.add(Observable.zip(mediaApiService.getStreamingConferences(),
mediaApiService.getConferences(),
(liveConferences, conferencesWrapper) -> {
ArrayList<Object> list = new ArrayList<>();
list.add(liveConferences);
list.add(conferencesWrapper);
return list;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(objects -> {
List<LiveConference> liveConferences = (List<LiveConference>) objects.get(0);
ConferencesWrapper conferences = (ConferencesWrapper) objects.get(1);
if (BuildConfig.DEBUG) {
liveConferences.add(LiveConference.getDummyObject());
}
streamsAvailable = liveConferences.size() > 0;
addStreams(cardPresenter, liveConferences);
addRecordings(cardPresenter, conferences);
errorFragment.dismiss();
setOnItemViewClickedListener(new ItemViewClickedListener(this));
setAdapter(mRowsAdapter);
setSelectedPosition(1);
}));
});
mDisposables.add(disposable);
}
private void addRecordings(CardPresenter cardPresenter, ConferencesWrapper conferences) {
mConferencesSection = new SectionRow(getString(R.string.conferences));
mRowsAdapter.add(mConferencesSection);
mConferences = conferences.getConferencesBySeries();
Set<String> keySet = mConferences.keySet();
for (String tag : ConferencesWrapper.getOrderedConferencesList()) {
if (keySet.contains(tag)) {
ListRow row = getRow(mConferences, cardPresenter, tag, "");
mRowsAdapter.add(row);
}
}
for (String tag : keySet) {
if (!ConferencesWrapper.getOrderedConferencesList().contains(tag)) {
mRowsAdapter.add(getRow(mConferences, cardPresenter, tag, ""));
}
}
}
private void addStreams(CardPresenter cardPresenter, List<LiveConference> liveConferences) {
if (liveConferences.size() > 0) {
HeaderItem streamingHeader = new HeaderItem(getString(R.string.livestreams));
mStreamingSection = new SectionRow(streamingHeader);
mRowsAdapter.add(0, mStreamingSection);
for (LiveConference con : liveConferences) {
int i = -1;
for (i = 0; i < con.getGroups().size(); i++) {
Group g = con.getGroups().get(i);
// setup header
String group = g.getGroup().length() > 0 ? g.getGroup() : con.getConference();
HeaderItem header = new HeaderItem(group);
header.setDescription(con.getConference() + " - " + con.getDescription());
header.setContentDescription(group);
// setup list
ArrayObjectAdapter listRowAdapter
= new ArrayObjectAdapter(cardPresenter);
listRowAdapter.addAll(listRowAdapter.size(), g.getRooms());
mRowsAdapter.add(i + 1, new ListRow(header, listRowAdapter));
}
// mRowsAdapter.add(i + 1, new DividerRow());
}
}
}
@Override
public void onResume() {
super.onResume();
List<WatchlistItem> watchlistItems
= Lists.newArrayList(WatchlistItem.findAll(WatchlistItem.class));
// setup and list items
updateWatchlist(watchlistItems);
}
private void showWatchlist() {
if(mWatchlistRow == null && mRecomendationsSectionsRow == null){
mRecomendationsSectionsRow = new SectionRow(getString(R.string.recomendations));
HeaderItem header = new HeaderItem(getString(R.string.watchlist));
mWatchlistRow = new ListRow(header, watchListAdapter);
}
int offset = getWatchlistOffset();
if(!mWatchlistVisible){
mRowsAdapter.add(offset, mRecomendationsSectionsRow);
mRowsAdapter.add(offset + 1, mWatchlistRow);
mWatchlistVisible = true;
}
}
private Disposable updateWatchlist(List<WatchlistItem> watchlistItems) {
return ((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mediaApiService -> {
showWatchlist();
watchListAdapter.clear();
if(watchlistItems.size() > 0){
// int i = Math.max(0,mRowsAdapter.indexOf(mConferencesSection));
// mRowsAdapter.add(i,mRecomendationsSectionsRow);
// mRowsAdapter.add(i+1,watchListAdapter);
Observable.fromIterable(watchlistItems)
.flatMap(watchlistItem -> mediaApiService.getEvent(watchlistItem.getEventId()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> watchListAdapter.add(event));
} else {
// watchListAdapter.add("Watchlist empty");
hideWatchlist();
}
});
}
private void hideWatchlist() {
if(mWatchlistVisible){
int i = mRowsAdapter.indexOf(mRecomendationsSectionsRow);
mRowsAdapter.removeItems(i,2);
mWatchlistVisible = false;
}
}
private int getWatchlistOffset() {
if(mRowsAdapter.size()>0 || streamsAvailable){
int i = mRowsAdapter.indexOf(mConferencesSection);
return Math.max(i,0);
} else {
return 0;
}
}
@Override
public void onStop() {
mDisposables.dispose();
super.onStop();
}
private ListRow getRow(Map<String, List<Conference>> conferences, CardPresenter cardPresenter, String tag, String description) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
listRowAdapter.addAll(0, conferences.get(tag));
HeaderItem header = new HeaderItem(ConferencesWrapper.getStringForTag(tag));
header.setDescription(description);
return new ListRow(header, listRowAdapter);
}
}

View file

@ -1,60 +0,0 @@
package de.nicidienase.chaosflix.leanback.fragments;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.VerticalGridFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.VerticalGridPresenter;
import java.util.Collections;
import java.util.List;
import de.nicidienase.chaosflix.leanback.CardPresenter;
import de.nicidienase.chaosflix.leanback.ItemViewClickedListener;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import io.reactivex.android.schedulers.AndroidSchedulers;
/**
* Created by felix on 20.03.17.
*/
public class ConferencesGridFragment extends VerticalGridFragment {
private static final int NUM_COLUMNS = 5;
private static final String TAG = ConferencesGridFragment.class.getSimpleName();
private final ArrayObjectAdapter mConferenceAdapter =
new ArrayObjectAdapter(new CardPresenter());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.subscribe(mediaApiService -> {
mediaApiService.getConferences()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(conferences -> {
List<Conference> conferenceList = conferences.getConferences();
Collections.sort(conferenceList);
Collections.reverse(conferenceList);
mConferenceAdapter.addAll(0, conferenceList);
setAdapter(mConferenceAdapter);
});
});
setTitle("Chaosflix");
VerticalGridPresenter gridPresenter = new VerticalGridPresenter();
gridPresenter.setNumberOfColumns(NUM_COLUMNS);
setGridPresenter(gridPresenter);
// After 500ms, start the animation to transition the cards into view.
new Handler().postDelayed(new Runnable() {
public void run() {
startEntranceTransition();
}
}, 500);
setOnItemViewClickedListener(new ItemViewClickedListener(this));
}
}

View file

@ -1,230 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.leanback.fragments;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.DisplayMetrics;
import android.util.Log;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.google.common.collect.Lists;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import de.nicidienase.chaosflix.leanback.CardPresenter;
import de.nicidienase.chaosflix.leanback.ItemViewClickedListener;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.leanback.activities.EventsActivity;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class EventsBrowseFragment extends BrowseFragment {
private static final String TAG = EventsBrowseFragment.class.getSimpleName();
private static final int BACKGROUND_UPDATE_DELAY = 300;
private static final int FRAGMENT = R.id.browse_fragment;
private final Handler mHandler = new Handler();
private ArrayObjectAdapter mRowsAdapter;
private Drawable mDefaultBackground;
private DisplayMetrics mMetrics;
private Timer mBackgroundTimer;
private URI mBackgroundURI;
private BackgroundManager mBackgroundManager;
private Conference mConference;
private int conferenceId;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onActivityCreated(savedInstanceState);
final BrowseErrorFragment errorFragment =
BrowseErrorFragment.showErrorFragment(getFragmentManager(), FRAGMENT);
conferenceId = this.getActivity().getIntent().getIntExtra(EventsActivity.CONFERENCE_ID, 0);
mConference = this.getActivity().getIntent().getParcelableExtra(EventsActivity.CONFERENCE);
((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.subscribe(mediaApiService -> {
mediaApiService.getConference(mConference.getApiID())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(t -> errorFragment.setErrorContent(t.getMessage()))
.subscribe(conference -> {
mConference = conference;
setupUIElements();
loadRows();
errorFragment.dismiss();
});
});
prepareBackgroundManager();
setOnItemViewClickedListener(new ItemViewClickedListener(this));
setOnItemViewSelectedListener(new ItemViewSelectedListener());
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mBackgroundTimer) {
Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString());
mBackgroundTimer.cancel();
}
}
private void loadRows() {
HashMap<String, List<Event>> eventsByTags = mConference.getEventsByTags();
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
CardPresenter cardPresenter = new CardPresenter();
List<Event> other = new LinkedList<Event>();
List<String> keys = Lists.newArrayList(eventsByTags.keySet());
Collections.sort(keys);
for (String tag : keys) {
List<Event> items = eventsByTags.get(tag);
Collections.sort(items);
if (android.text.TextUtils.isDigitsOnly(tag)) {
other.addAll(items);
} else {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
listRowAdapter.addAll(0, items);
HeaderItem header = new HeaderItem(tag);
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
}
if (other.size() > 0) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
listRowAdapter.addAll(0, other);
HeaderItem header = new HeaderItem("other");
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
setAdapter(mRowsAdapter);
}
private void prepareBackgroundManager() {
mBackgroundManager = BackgroundManager.getInstance(getActivity());
mBackgroundManager.attach(getActivity().getWindow());
mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
private void setupUIElements() {
Glide.with(getActivity())
.load(mConference.getLogoUrl())
.centerCrop()
.error(mDefaultBackground)
.into(new SimpleTarget<GlideDrawable>(432, 243) {
@Override
public void onResourceReady(GlideDrawable resource,
GlideAnimation<? super GlideDrawable>
glideAnimation) {
setBadgeDrawable(resource);
}
});
setTitle(mConference.getTitle()); // Badge, when set, takes precedent
// over title
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// set fastLane (or headers) background color
setBrandColor(getResources().getColor(R.color.fastlane_background));
// set search icon color
setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
}
protected void updateBackground(String uri) {
int width = mMetrics.widthPixels;
int height = mMetrics.heightPixels;
Glide.with(getActivity())
.load(uri)
.centerCrop()
.error(mDefaultBackground)
.into(new SimpleTarget<GlideDrawable>(width, height) {
@Override
public void onResourceReady(GlideDrawable resource,
GlideAnimation<? super GlideDrawable>
glideAnimation) {
mBackgroundManager.setDrawable(resource);
}
});
mBackgroundTimer.cancel();
}
private void startBackgroundTimer() {
if (null != mBackgroundTimer) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
}
private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof Event) {
try {
mBackgroundURI = new URI(((Event) item).getPosterUrl());
} catch (URISyntaxException e) {
e.printStackTrace();
}
// TODO make configurable (enable/disable)
// startBackgroundTimer();
}
}
}
private class UpdateBackgroundTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mBackgroundURI != null) {
updateBackground(mBackgroundURI.toString());
}
}
});
}
}
}

View file

@ -1,443 +0,0 @@
package de.nicidienase.chaosflix.leanback.fragments;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.DetailsFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.DetailsOverviewLogoPresenter;
import android.support.v17.leanback.widget.DetailsOverviewRow;
import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
import android.support.v17.leanback.widget.FullWidthDetailsOverviewSharedElementHelper;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.Presenter;
import android.app.AlertDialog;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.nicidienase.chaosflix.leanback.CardPresenter;
import de.nicidienase.chaosflix.leanback.EventDetailsDescriptionPresenter;
import de.nicidienase.chaosflix.leanback.ItemViewClickedListener;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.leanback.activities.ConferencesActivity;
import de.nicidienase.chaosflix.leanback.activities.DetailsActivity;
import de.nicidienase.chaosflix.leanback.activities.EventDetailsActivity;
import de.nicidienase.chaosflix.leanback.activities.PlayerActivity;
import de.nicidienase.chaosflix.shared.entities.WatchlistItem;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.recording.Recording;
import de.nicidienase.chaosflix.shared.entities.streaming.Group;
import de.nicidienase.chaosflix.shared.entities.streaming.LiveConference;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
import de.nicidienase.chaosflix.shared.entities.streaming.Stream;
import de.nicidienase.chaosflix.shared.entities.streaming.StreamUrl;
import de.nicidienase.chaosflix.shared.network.MediaApiService;
import io.reactivex.android.schedulers.AndroidSchedulers;
/**
* Created by felix on 18.03.17.
*/
public class EventsDetailsFragment extends DetailsFragment {
private static final int DETAIL_THUMB_WIDTH = 254;
private static final int DETAIL_THUMB_HEIGHT = 143;
private static final int NUM_RELATED_TALKS = 5;
private static final int NUM_RANDOM_TALKS = NUM_RELATED_TALKS;
private static final String TAG = EventsDetailsFragment.class.getSimpleName();
public static final int FRAGMENT = R.id.details_fragment;
public static final int DUMMY_ID = 1646465164;
public static final int DEFAULT_DRAWABLE = R.drawable.default_background;
private static final long ADD_WATCHLIST_ACTION = 1646465165;
private static final long REMOVE_WATCHLIST_ACTION = 1646465166;
private Event mSelectedEvent;
private MediaApiService mMediaApiService;
private Room mRoom;
private int eventType;
private ArrayList<StreamUrl> streamUrlList;
private BackgroundManager mBackgroundmanager;
private DisplayMetrics mMetrics;
private WatchlistItem mWatchlistItem;
private ArrayObjectAdapter mRecordingActionsAdapter;
private OnActionClickedListener mOnActionClickedListener = new OnActionClickedListener() {
@Override
public void onActionClicked(Action action) {
Log.d(TAG, "OnActionClicked");
if (action.getId() == ADD_WATCHLIST_ACTION) {
new WatchlistItem(mSelectedEvent.getApiID()).save();
mRecordingActionsAdapter.replace(0, new Action(REMOVE_WATCHLIST_ACTION, EventsDetailsFragment.this.getString(R.string.remove_from_watchlist)));
SharedPreferences preferences = getActivity().getSharedPreferences(getString(R.string.watchlist_preferences_key), Context.MODE_PRIVATE);
if(preferences.getBoolean(getString(R.string.watchlist_dialog_needed), true)) { // new item
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.watchlist_message);
builder.setNegativeButton(R.string.return_to_homescreen,(dialog, which) -> {
Intent i = new Intent(getActivity(), ConferencesActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
getActivity().finish();
});
builder.setPositiveButton("OK",(dialog, which) -> {});
SharedPreferences.Editor edit = preferences.edit();
edit.putBoolean(getString(R.string.watchlist_dialog_needed), false);
edit.commit();
builder.create().show();
}
} else if (action.getId() == REMOVE_WATCHLIST_ACTION) {
if (mWatchlistItem != null) {
mWatchlistItem.delete();
}
mRecordingActionsAdapter.replace(0, new Action(ADD_WATCHLIST_ACTION, EventsDetailsFragment.this.getString(R.string.add_to_watchlist)));
} else {
Intent i = new Intent(EventsDetailsFragment.this.getActivity(), PlayerActivity.class);
i.putExtra(DetailsActivity.TYPE, eventType);
if (eventType == DetailsActivity.TYPE_RECORDING) {
i.putExtra(DetailsActivity.EVENT, mSelectedEvent);
if (action.getId() == DUMMY_ID) {
Recording dummy = new Recording();
dummy.setRecordingUrl("https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8");
dummy.setMimeType("video/hls");
dummy.setLanguage("eng");
dummy.setHighQuality(true);
i.putExtra(DetailsActivity.RECORDING, dummy);
} else {
for (Recording r : mSelectedEvent.getRecordings()) {
if (r.getApiID() == action.getId()) {
i.putExtra(DetailsActivity.RECORDING, r);
break;
}
}
}
} else if (eventType == DetailsActivity.TYPE_STREAM) {
i.putExtra(DetailsActivity.ROOM, mRoom);
StreamUrl streamUrl = EventsDetailsFragment.this.getStreamUrlForActionId((int) action.getId());
if (streamUrl != null) {
i.putExtra(DetailsActivity.STREAM_URL, streamUrl);
} else {
// TODO handle missing Stream
return;
}
}
EventsDetailsFragment.this.getActivity().startActivity(i);
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
prepareBackgroundManager();
final BrowseErrorFragment browseErrorFragment =
BrowseErrorFragment.showErrorFragment(getFragmentManager(), FRAGMENT);
eventType = getActivity().getIntent().getIntExtra(DetailsActivity.TYPE, -1);
if (eventType == DetailsActivity.TYPE_RECORDING) {
mSelectedEvent = getActivity().getIntent()
.getParcelableExtra(DetailsActivity.EVENT);
mWatchlistItem = WatchlistItem.findById(WatchlistItem.class, mSelectedEvent.getApiID());
} else if (eventType == DetailsActivity.TYPE_STREAM) {
mRoom = getActivity().getIntent()
.getParcelableExtra(DetailsActivity.ROOM);
}
FullWidthDetailsOverviewRowPresenter mDetailsPresenter
= setupDetailsOverviewRowPresenter();
ClassPresenterSelector mPresenterSelector = new ClassPresenterSelector();
mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, mDetailsPresenter);
mPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter());
final ArrayObjectAdapter adapter = new ArrayObjectAdapter(mPresenterSelector);
((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.doOnError(t -> browseErrorFragment.setErrorContent(t.getMessage()))
.subscribe(mediaApiService -> {
mMediaApiService = mediaApiService;
if (eventType == DetailsActivity.TYPE_RECORDING) {
final DetailsOverviewRow detailsOverviewRow = setupDetailsOverviewRow(mSelectedEvent);
mediaApiService.getEvent(mSelectedEvent.getApiID())
.doOnError(t -> browseErrorFragment.setErrorContent(t.getMessage()))
.subscribe(event -> {
mSelectedEvent = event;
mRecordingActionsAdapter =
getRecordingActionsAdapter(mSelectedEvent.getRecordings());
detailsOverviewRow.setActionsAdapter(mRecordingActionsAdapter);
adapter.add(detailsOverviewRow);
mediaApiService.getConference(
mSelectedEvent.getConferenceId())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(conference -> {
String tag = null;
if (mSelectedEvent.getTags().size() > 0) {
tag = mSelectedEvent.getTags().get(0);
List<Event> relatedEvents = conference.getEventsByTags().get(tag);
relatedEvents.remove(mSelectedEvent);
Collections.shuffle(relatedEvents);
if (relatedEvents.size() > 5) {
relatedEvents = relatedEvents.subList(0, NUM_RELATED_TALKS);
}
ArrayObjectAdapter relatedEventsAdapter
= new ArrayObjectAdapter(new CardPresenter());
relatedEventsAdapter.addAll(0, relatedEvents);
HeaderItem header = new HeaderItem(getString(R.string.random_talks_on_this_track));
adapter.add(new ListRow(header, relatedEventsAdapter));
}
List<Event> selectedEvents = getRandomEvents(conference, tag);
if (selectedEvents.size() > 0) {
ArrayObjectAdapter randomEventAdapter
= new ArrayObjectAdapter(new CardPresenter());
randomEventAdapter.addAll(0, selectedEvents);
HeaderItem header = new HeaderItem(getString(R.string.random_talks));
adapter.add(new ListRow(header, randomEventAdapter));
}
setOnItemViewClickedListener(new ItemViewClickedListener(this));
setAdapter(adapter);
browseErrorFragment.dismiss();
});
});
} else if (eventType == DetailsActivity.TYPE_STREAM) {
mediaApiService.getStreamingConferences()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(liveConferences -> {
if(mRoom.getStreams() == null){
mRoom = getRoom(mRoom, liveConferences);
}
final DetailsOverviewRow detailsOverviewRow = setupDetailsOverviewRow(mRoom);
ArrayObjectAdapter actionsAdapter = getStreamActionsAdapter(mRoom.getStreams());
detailsOverviewRow.setActionsAdapter(actionsAdapter);
adapter.add(detailsOverviewRow);
setOnItemViewClickedListener(new ItemViewClickedListener(EventsDetailsFragment.this));
setAdapter(adapter);
browseErrorFragment.dismiss();
// TODO add other streams
});
}
});
}
@Override
public void onStart() {
super.onStart();
startEntranceTransition();
}
@Override
public void onStop() {
mBackgroundmanager.release();
super.onStop();
}
private void prepareBackgroundManager() {
mBackgroundmanager = BackgroundManager.getInstance(getActivity());
mBackgroundmanager.attach(getActivity().getWindow());
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
private void updateBackground(String uri) {
Glide.with(this)
.load(uri)
.asBitmap()
.centerCrop()
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
mBackgroundmanager.setBitmap(resource);
}
});
}
private Room getRoom(Room room, List<LiveConference> liveConferences) {
for (LiveConference con : liveConferences) {
for (Group g : con.getGroups()) {
for (Room r : g.getRooms()) {
if (r.getSlug().equals(room.getSlug())) {
return r;
}
}
}
}
return null;
}
@NonNull
private List<Event> getRandomEvents(Conference conference, String tag) {
List<Event> randomEvents = conference.getEvents();
Collections.shuffle(randomEvents);
List<Event> selectedEvents;
if (tag != null) {
selectedEvents = new ArrayList<Event>();
for (Event e : randomEvents) {
if (!e.getTags().contains(tag)) {
selectedEvents.add(e);
}
if (selectedEvents.size() == 5) {
break;
}
}
} else {
selectedEvents = randomEvents.subList(0, NUM_RANDOM_TALKS);
}
return selectedEvents;
}
private FullWidthDetailsOverviewRowPresenter setupDetailsOverviewRowPresenter() {
FullWidthDetailsOverviewRowPresenter mDetailsPresenter = new FullWidthDetailsOverviewRowPresenter(
new EventDetailsDescriptionPresenter(getActivity()),
new EventDetailsOverviewLogoPresenter());
mDetailsPresenter.setBackgroundColor(getResources().getColor(R.color.selected_background));
mDetailsPresenter.setInitialState(FullWidthDetailsOverviewRowPresenter.STATE_HALF);
mDetailsPresenter.setAlignmentMode(FullWidthDetailsOverviewRowPresenter.ALIGN_MODE_START);
FullWidthDetailsOverviewSharedElementHelper helper
= new FullWidthDetailsOverviewSharedElementHelper();
helper.setSharedElementEnterTransition(getActivity(),
EventDetailsActivity.SHARED_ELEMENT_NAME);
mDetailsPresenter.setListener(helper);
prepareEntranceTransition();
mDetailsPresenter.setOnActionClickedListener(mOnActionClickedListener);
return mDetailsPresenter;
}
private DetailsOverviewRow setupDetailsOverviewRow(Object event) {
final DetailsOverviewRow row = new DetailsOverviewRow(event);
String thumbUrl;
if (event instanceof Event) {
thumbUrl = ((Event) event).getThumbUrl();
} else {
thumbUrl = ((Room) event).getThumb();
}
Glide.with(getActivity())
.load(thumbUrl)
.asBitmap()
.into(new SimpleTarget<Bitmap>(DETAIL_THUMB_WIDTH, DETAIL_THUMB_HEIGHT) {
@Override
public void onResourceReady(Bitmap resource,
GlideAnimation<? super Bitmap> glideAnimation) {
row.setImageBitmap(getActivity(), resource);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
// super.onLoadFailed(e, errorDrawable);
row.setImageDrawable(getResources().getDrawable(DEFAULT_DRAWABLE));
}
});
return row;
}
private ArrayObjectAdapter getRecordingActionsAdapter(List<Recording> recordings) {
ArrayObjectAdapter actionsAdapter = new ArrayObjectAdapter();
if(mWatchlistItem != null){
actionsAdapter.add(new Action(REMOVE_WATCHLIST_ACTION,getString(R.string.remove_from_watchlist)));
} else {
actionsAdapter.add(new Action(ADD_WATCHLIST_ACTION,getString(R.string.add_to_watchlist)));
}
if (recordings != null) {
for (int i = 0; i < recordings.size(); i++) {
Recording recording = recordings.get(i);
if (recording.getMimeType().startsWith("video/") || true) {
String quality = recording.isHighQuality() ? "HD" : "SD";
String title = quality + " (" + recording.getLanguage() + ")";
actionsAdapter.add(new Action(recording.getApiID(), title, recording.getMimeType().substring(6)));
}
}
} else {
actionsAdapter.add(new Action(DUMMY_ID, "Dummy", "HLS"));
}
return actionsAdapter;
}
private ArrayObjectAdapter getStreamActionsAdapter(List<Stream> streams) {
ArrayObjectAdapter actionsAdapter = new ArrayObjectAdapter();
streamUrlList = new ArrayList<StreamUrl>();
for (Stream s : streams) {
if (s.getType().equals("video") || true)
for (String key : s.getUrls().keySet()) {
StreamUrl url = s.getUrls().get(key);
int index = streamUrlList.size();
streamUrlList.add(url);
actionsAdapter.add(new Action(index, s.getDisplay(), url.getDisplay()));
}
}
return actionsAdapter;
}
private StreamUrl getStreamUrlForActionId(int actionId) {
if (streamUrlList != null && streamUrlList.size() > actionId) {
return streamUrlList.get(actionId);
} else {
return null;
}
}
static class EventDetailsOverviewLogoPresenter extends DetailsOverviewLogoPresenter {
static class ViewHolder extends DetailsOverviewLogoPresenter.ViewHolder {
public ViewHolder(View view) {
super(view);
}
public FullWidthDetailsOverviewRowPresenter getParentPresenter() {
return mParentPresenter;
}
public FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder() {
return mParentViewHolder;
}
}
@Override
public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
ImageView imageView = (ImageView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.lb_fullwidth_details_overview_logo, parent, false);
Resources res = parent.getResources();
int width = res.getDimensionPixelSize(R.dimen.detail_thumb_width);
int height = res.getDimensionPixelSize(R.dimen.detail_thumb_height);
imageView.setLayoutParams(new ViewGroup.MarginLayoutParams(width, height));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
return new ViewHolder(imageView);
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
DetailsOverviewRow row = (DetailsOverviewRow) item;
ImageView imageView = ((ImageView) viewHolder.view);
imageView.setImageDrawable(row.getImageDrawable());
if (isBoundToImage((ViewHolder) viewHolder, row)) {
EventDetailsOverviewLogoPresenter.ViewHolder vh =
(EventDetailsOverviewLogoPresenter.ViewHolder) viewHolder;
vh.getParentPresenter().notifyOnBindLogo(vh.getParentViewHolder());
}
}
}
}

View file

@ -1,484 +0,0 @@
package de.nicidienase.chaosflix.leanback.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.support.v17.leanback.app.PlaybackFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.support.v17.leanback.widget.Row;
import android.util.Log;
import java.util.List;
import de.nicidienase.chaosflix.leanback.CardPresenter;
import de.nicidienase.chaosflix.leanback.ItemViewClickedListener;
import de.nicidienase.chaosflix.leanback.PlaybackHelper;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.leanback.activities.DetailsActivity;
import de.nicidienase.chaosflix.shared.entities.PlaybackProgress;
import de.nicidienase.chaosflix.shared.entities.recording.Event;
import de.nicidienase.chaosflix.shared.entities.recording.Recording;
import de.nicidienase.chaosflix.shared.entities.streaming.Room;
import de.nicidienase.chaosflix.shared.entities.streaming.StreamUrl;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS;
import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS;
/**
* Created by felix on 26.03.17.
*/
public class OverlayFragment extends PlaybackFragment {
private static final String TAG = OverlayFragment.class.getSimpleName();
private static final long RESUME_SKIP = 5; // seconds to skip back on resume
public static final int MAX_REMAINING = 30;
private Recording mSelectedRecording;
private Event mSelectedEvent;
private PlaybackProgress mPlaybackProgress;
private Room mSelectedRoom;
private PlaybackHelper mHelper;
private PlaybackControlListener mCallback;
private ArrayObjectAdapter mRowsAdapter;
private MediaSession mSession;
private boolean mHasAudioFocus;
private boolean mPauseTransient;
private AudioManager mAudioManager;
private MediaController mMediaControler;
private int eventType;
private StreamUrl mSelectedStream;
private MediaController.Callback mMediaControllerCallback;
private CompositeDisposable mDisposables = new CompositeDisposable();
private final AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
abandonAudioFocus();
pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (mHelper.isMediaPlaying()) {
pause();
mPauseTransient = true;
}
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mCallback.mute(true);
break;
case AudioManager.AUDIOFOCUS_GAIN:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
if (mPauseTransient) {
play();
}
mCallback.mute(false);
break;
}
}
};
public interface PlaybackControlListener {
void play();
void pause();
void playPause();
void setVideoSource(String source);
void skipForward(int sec);
void skipBackward(int sec);
void seekTo(long sec);
boolean isMediaPlaying();
long getLength();
long getCurrentPosition();
long getBufferedPosition();
void releasePlayer();
void mute(boolean state);
void nextAudioStream();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "OnCreate");
Intent intent = getActivity()
.getIntent();
eventType = intent.getIntExtra(DetailsActivity.TYPE, -1);
if (eventType == DetailsActivity.TYPE_RECORDING) {
mSelectedEvent = intent.getParcelableExtra(DetailsActivity.EVENT);
mSelectedRecording = intent.getParcelableExtra(DetailsActivity.RECORDING);
mHelper = new PlaybackHelper(getActivity(), this, mSelectedEvent, mSelectedRecording);
List<PlaybackProgress> progressList = PlaybackProgress.find(PlaybackProgress.class, "event_id = ?", String.valueOf(mSelectedEvent.getApiID()));
if (progressList.size() > 0) {
mPlaybackProgress = progressList.get(0);
}
} else if (eventType == DetailsActivity.TYPE_STREAM) {
mSelectedRoom = intent.getParcelableExtra(DetailsActivity.ROOM);
mSelectedStream = intent.getParcelableExtra(DetailsActivity.STREAM_URL);
mHelper = new PlaybackHelper(getActivity(), this, mSelectedRoom, mSelectedStream);
} else {
Log.d(TAG, "No Media found, finishing");
getActivity().finish();
}
mAudioManager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
setBackgroundType(PlaybackFragment.BG_LIGHT);
setFadingEnabled(false);
}
@Override
public synchronized void onStart() {
super.onStart();
Log.d(TAG, "OnStart");
PlaybackControlsRowPresenter playbackControlsRowPresenter = mHelper.createControlsRowAndPresenter();
PlaybackControlsRow controlsRow = mHelper.getControlsRow();
mMediaControllerCallback = mHelper.createMediaControllerCallback();
requestAudioFocus();
mMediaControler = getActivity().getMediaController();
if (mMediaControler != null) {
mMediaControler.registerCallback(mMediaControllerCallback);
} else {
Log.d(TAG, "MediaController is null");
getActivity().finish();
}
ClassPresenterSelector ps = new ClassPresenterSelector();
ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
ps.addClassPresenter(ListRow.class, new ListRowPresenter());
mRowsAdapter = new ArrayObjectAdapter(ps);
mRowsAdapter.add(controlsRow);
if (eventType == DetailsActivity.TYPE_RECORDING) {
if (mSelectedEvent.getMetadata() != null && mSelectedEvent.getMetadata().getRelated() != null) {
mRowsAdapter.add(getRelatedItems());
setOnItemViewClickedListener(new ItemViewClickedListener(this));
}
} else if (eventType == DetailsActivity.TYPE_STREAM) {
// TODO add other streams as related events
}
setAdapter(mRowsAdapter);
if (mCallback != null && eventType == DetailsActivity.TYPE_STREAM) {
mCallback.setVideoSource(mSelectedStream.getUrl());
} else if (mCallback != null && eventType == DetailsActivity.TYPE_RECORDING) {
mCallback.setVideoSource(mSelectedRecording.getRecordingUrl());
} else {
Log.d(TAG, "Callback not set or not event/stream");
}
requestAudioFocus();
if (mPlaybackProgress != null) {
showContinueOrRestartDialog();
} else {
play();
}
}
private void showContinueOrRestartDialog() {
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
.setMessage(R.string.resume_question)
.setPositiveButton(R.string.start_again, (dialog, which) -> play())
.setNegativeButton(R.string.resume, (dialog, which) -> {
mCallback.seekTo(getProgress());
play();
})
.create();
alertDialog.show();
}
private long getProgress() {
long progress = mPlaybackProgress.getProgress() / 1000;
if (progress >= mSelectedEvent.getLength()) {
return 0;
} else {
return Math.max(0, progress - RESUME_SKIP);
}
}
private Row getRelatedItems() {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
final long[] related = mSelectedEvent.getMetadata().getRelated();
mDisposables.add(((ChaosflixBaseActivity) getActivity()).getApiServiceObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
mediaApiService -> {
for (long id : related) {
mDisposables.add(mediaApiService.getEvent(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> listRowAdapter.add(event)));
}
listRowAdapter.notifyArrayItemRangeChanged(0, listRowAdapter.size());
}
)
);
HeaderItem header = new HeaderItem(0, getString(R.string.related_talks));
return new ListRow(header, listRowAdapter);
}
public boolean isMediaPlaying() {
if (mCallback != null) {
return mCallback.isMediaPlaying();
}
return false;
}
public int getCurrentPosition() {
if (eventType == DetailsActivity.TYPE_RECORDING) {
if (mCallback != null) {
return (int) mCallback.getCurrentPosition();
}
}
return 0;
}
private long getCurrentPositionLong() {
if (eventType == DetailsActivity.TYPE_RECORDING) {
if (mCallback != null) {
return mCallback.getCurrentPosition();
}
}
return 0;
}
public long getCurrentBufferedPosition() {
if (mCallback != null) {
return mCallback.getBufferedPosition();
}
return 0;
}
@Override
public void onStop() {
super.onStop();
if (mSession != null) {
mSession.release();
}
mDisposables.dispose();
abandonAudioFocus();
}
@Override
public void onPause() {
super.onPause();
if (mSelectedEvent != null) {
if (mPlaybackProgress != null) {
if ((mSelectedEvent.getLength() - mCallback.getCurrentPosition() / 1000) > MAX_REMAINING) {
mPlaybackProgress.setProgress(mCallback.getCurrentPosition());
mPlaybackProgress.save();
} else {
mPlaybackProgress.delete();
}
} else if ((mSelectedEvent.getLength() - mCallback.getCurrentPosition() / 1000) > MAX_REMAINING) {
mPlaybackProgress = new PlaybackProgress(mSelectedEvent.getApiID(),
mCallback.getCurrentPosition(), mSelectedRecording.getApiID());
mPlaybackProgress.save();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mSession != null) {
mSession.release();
}
mCallback.releasePlayer();
mHelper.onStop();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
setupMediaSession(context);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
setupMediaSession(activity);
}
@SuppressWarnings("WrongConstant")
private void setupMediaSession(Context context) {
Log.d(TAG, "OnAttach");
if (context instanceof PlaybackControlListener) {
mCallback = (PlaybackControlListener) context;
} else {
throw (new RuntimeException("Activity must implement PlaybackControlListener"));
}
if (mSession == null) {
mSession = new MediaSession(getActivity(), "chaosflix");
mSession.setCallback(new ChaosflixSessionCallback());
mSession.setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setActive(true);
setPlaybackState(PlaybackState.STATE_NONE);
getActivity().setMediaController(
new MediaController(getActivity(), mSession.getSessionToken()));
}
}
@SuppressWarnings("WrongConstant")
private void setPlaybackState(int state) {
long currentPosition = getCurrentPositionLong();
PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
.setActions(getAvailableActions(state))
.setState(PlaybackState.STATE_PLAYING, currentPosition, 0);
if (mSession != null) {
mSession.setPlaybackState(stateBuilder.build());
}
}
private int getPlaybackState() {
Activity activity = getActivity();
if (activity != null) {
PlaybackState state = activity.getMediaController().getPlaybackState();
if (state != null) {
return state.getState();
} else {
return PlaybackState.STATE_NONE;
}
}
return PlaybackState.STATE_NONE;
}
private long getAvailableActions(int nextState) {
long actions = PlaybackState.ACTION_PLAY |
PlaybackState.ACTION_SKIP_TO_NEXT |
PlaybackState.ACTION_SKIP_TO_PREVIOUS |
PlaybackState.ACTION_FAST_FORWARD |
PlaybackState.ACTION_REWIND |
PlaybackState.ACTION_PAUSE;
if (nextState == PlaybackState.STATE_PLAYING) {
actions |= PlaybackState.ACTION_PAUSE;
}
return actions;
}
private void requestAudioFocus() {
if (mHasAudioFocus) {
return;
}
int result = mAudioManager.requestAudioFocus(mOnAudioFocusChangeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mHasAudioFocus = true;
} else {
pause();
}
}
private void abandonAudioFocus() {
mHasAudioFocus = false;
mAudioManager.abandonAudioFocus(mOnAudioFocusChangeListener);
}
private void play() {
if (mCallback != null) {
setPlaybackState(PlaybackState.STATE_PLAYING);
setFadingEnabled(true);
mCallback.play();
}
}
private void pause() {
if (mCallback != null) {
setPlaybackState(PlaybackState.STATE_PAUSED);
setFadingEnabled(false);
mCallback.pause();
}
}
private void rewind() {
if (mCallback != null) {
int prevState = getPlaybackState();
setPlaybackState(PlaybackState.STATE_FAST_FORWARDING);
mCallback.skipBackward(30);
setPlaybackState(prevState);
}
}
private void fastForward() {
if (mCallback != null) {
int prevState = getPlaybackState();
setPlaybackState(PlaybackState.STATE_FAST_FORWARDING);
mCallback.skipForward(30);
setPlaybackState(prevState);
}
}
private class ChaosflixSessionCallback extends MediaSession.Callback {
@Override
public void onPlay() {
play();
}
@Override
public void onPause() {
pause();
}
@Override
public void onFastForward() {
fastForward();
}
@Override
public void onRewind() {
rewind();
}
@Override
public void onSkipToNext() {
mCallback.skipForward(5 * 60);
}
@Override
public void onSkipToPrevious() {
mCallback.skipBackward(5 * 60);
}
@Override
public void onSeekTo(long pos) {
mCallback.seekTo(pos);
}
}
}

View file

@ -1,70 +0,0 @@
package de.nicidienase.chaosflix.shared;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import de.nicidienase.chaosflix.shared.network.MediaApiService;
import io.reactivex.Single;
/**
* Created by felix on 24.03.17.
*/
public class ChaosflixBaseActivity extends AppCompatActivity {
private MediaApiService mMediaApiService = null;
private ServiceConnection conn;
private boolean mConnected = false;
private String serverUrl = null;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
serverUrl = getIntent().getStringExtra("server_url");
}
@Override
protected void onDestroy() {
if (mConnected) {
unbindService(conn);
conn = null;
}
super.onDestroy();
}
public Single<MediaApiService> getApiServiceObservable() {
Intent s = new Intent(this, MediaApiService.class);
if (serverUrl != null) {
s.putExtra(MediaApiService.RECORDING_URL, serverUrl);
s.putExtra(MediaApiService.STREAMING_URL, serverUrl);
}
return Single.create(e -> {
if (mMediaApiService != null) {
e.onSuccess(mMediaApiService);
} else {
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mConnected = true;
mMediaApiService = ((MediaApiService.LocalBinder) service).getService();
e.onSuccess(mMediaApiService);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMediaApiService = null;
mConnected = false;
}
};
ChaosflixBaseActivity.this.bindService(s, conn, Context.BIND_AUTO_CREATE);
}
});
}
}

View file

@ -1,64 +0,0 @@
package de.nicidienase.chaosflix.touch;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.shared.entities.recording.ConferencesWrapper;
import de.nicidienase.chaosflix.touch.fragments.ConferenceFragment;
/**
* Created by felix on 18.09.17.
*/
public class ConferenceGroupsFragmentPager extends FragmentPagerAdapter {
private static final String TAG = ConferenceGroupsFragmentPager.class.getSimpleName();
private List<String> orderedConferencesList = new ArrayList<>();
private Map<String, List<Conference>> mConferenceMap;
public ConferenceGroupsFragmentPager(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
ConferenceFragment conferenceFragment = new ConferenceFragment();
List<Conference> conferences = mConferenceMap.get(orderedConferencesList.get(position));
conferenceFragment.setContent(conferences);
return conferenceFragment;
}
@Override
public int getCount() {
return orderedConferencesList.size();
}
@Override
public CharSequence getPageTitle(int position) {
String string = ConferencesWrapper.getStringForTag(orderedConferencesList.get(position));
Log.d(TAG,string);
return string;
}
public void setContent(Map<String, List<Conference>> conferenceMap) {
mConferenceMap = conferenceMap;
// orderedConferencesList = new ArrayList<>(conferenceMap.keySet());
orderedConferencesList = new ArrayList<>();
for (String tag : ConferencesWrapper.getOrderedConferencesList()) {
if (conferenceMap.keySet().contains(tag)) {
orderedConferencesList.add(tag);
}
}
for (String tag : conferenceMap.keySet()) {
if (!orderedConferencesList.contains(tag)) {
orderedConferencesList.add(tag);
}
}
}
}

View file

@ -1,63 +0,0 @@
package de.nicidienase.chaosflix.touch.activities;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.util.Log;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.ChaosflixBaseActivity;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.touch.ConferenceGroupsFragmentPager;
import de.nicidienase.chaosflix.touch.fragments.ConferenceFragment;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* Created by felix on 17.09.17.
*/
public class ConferencesActivity extends ChaosflixBaseActivity implements ConferenceFragment.OnListFragmentInteractionListener{
private static final String TAG = ConferencesActivity.class.getSimpleName();
CompositeDisposable mDisposables = new CompositeDisposable();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.touch_browse);
setContentView(R.layout.tab_layout);
Disposable disposable = getApiServiceObservable()
.subscribe(mediaApiService -> {
mediaApiService.getConferences()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(conferencesWrapper -> {
ConferenceGroupsFragmentPager fragmentPager
= new ConferenceGroupsFragmentPager(getSupportFragmentManager());
fragmentPager.setContent(conferencesWrapper.getConferencesBySeries());
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(fragmentPager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(pager);
});
});
mDisposables.add(disposable);
}
@Override
protected void onStop() {
super.onStop();
mDisposables.dispose();
}
@Override
public void onListFragmentInteraction(Conference item) {
Log.d(TAG,"ListItem clicked");
}
}

View file

@ -1,115 +0,0 @@
package de.nicidienase.chaosflix.touch.fragments;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import java.util.ArrayList;
import java.util.List;
/**
* A fragment representing a list of Items.
* <p/>
* Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener}
* interface.
*/
public class ConferenceFragment extends Fragment {
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
private List<Conference> mConferences = new ArrayList<>();
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public ConferenceFragment() {
}
public void setContent(List<Conference> conferences){
mConferences = conferences;
}
// TODO: Customize parameter initialization
@SuppressWarnings("unused")
public static ConferenceFragment newInstance(int columnCount) {
ConferenceFragment fragment = new ConferenceFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_conference_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new ConferenceRecyclerViewAdapter(mConferences, mListener));
}
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
void onListFragmentInteraction(Conference item);
}
}

View file

@ -1,84 +0,0 @@
package de.nicidienase.chaosflix.touch.fragments;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import org.w3c.dom.Text;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.shared.entities.recording.Conference;
import de.nicidienase.chaosflix.touch.fragments.ConferenceFragment.OnListFragmentInteractionListener;
import java.util.List;
public class ConferenceRecyclerViewAdapter extends RecyclerView.Adapter<ConferenceRecyclerViewAdapter.ViewHolder> {
private final List<Conference> mConferences;
private final OnListFragmentInteractionListener mListener;
public ConferenceRecyclerViewAdapter(List<Conference> items, OnListFragmentInteractionListener listener) {
mConferences = items;
mListener = listener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_conference, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mConferences.get(position);
holder.mTitleText.setText(mConferences.get(position).getTitle());
holder.mAcronym.setText(mConferences.get(position).getAcronym());
Glide.with(holder.mIcon.getContext())
.load(mConferences.get(position).getLogoUrl())
.fitCenter()
.into(holder.mIcon);
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.mItem);
}
}
});
}
@Override
public int getItemCount() {
return mConferences.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final ImageView mIcon;
public final TextView mTitleText;
public final TextView mAcronym;
public Conference mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mIcon = (ImageView) view.findViewById(R.id.imageView);
mTitleText = (TextView) view.findViewById(R.id.title_text);
mAcronym = (TextView) view.findViewById(R.id.acronym_text);
}
@Override
public String toString() {
return super.toString() + " '" + mTitleText.getText() + "'";
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-270"
android:endColor="@color/background_gradient_end"
android:startColor="@color/background_gradient_start"/>
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-270"
android:endColor="@color/background_gradient_end"
android:startColor="@color/background_gradient_start"/>
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
android:width="24dp">
<path
android:fillColor="#FFFFFF"
android:pathData="M18,3v2h-2L16,3L8,3v2L6,5L6,3L4,3v18h2v-2h2v2h8v-2h2v2h2L20,3h-2zM8,17L6,17v-2h2v2zM8,13L6,13v-2h2v2zM8,9L6,9L6,7h2v2zM18,17h-2v-2h2v2zM18,13h-2v-2h2v2zM18,9h-2L16,7h2v2z"/>
</vector>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
android:width="24dp">
<path
android:fillColor="#FFFFFF"
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="90"
android:centerColor="#00000000"
android:endColor="#B2000000"
android:startColor="#B2000000"
android:type="linear"/>
</shape>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="de.nicidienase.chaosflix.leanback.fragments.ConferencesBrowseFragment"
android:id="@+id/browse_fragment"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="de.nicidienase.chaosflix.leanback.fragments.ConferencesGridFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment android:id="@+id/details_fragment"
android:name="de.nicidienase.chaosflix.leanback.fragments.EventsDetailsFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".leanback.activities.DetailsActivity"
tools:deviceIds="tv"/>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment android:id="@+id/browse_fragment"
android:name="de.nicidienase.chaosflix.leanback.fragments.EventsBrowseFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".leanback.activities.EventsActivity"
tools:deviceIds="tv"
tools:ignore="MergeRootFrame"/>

View file

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<fragment
android:name="de.nicidienase.chaosflix.leanback.fragments.OverlayFragment"
android:id="@+id/playback_controls_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="@+id/loading_player"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center"
android:indeterminate="true"
android:visibility="invisible"/>
</FrameLayout>

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
card_view:cardCornerRadius="4dp"
android:focusable="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="64dp"
android:layout_height="64dp"
app:srcCompat="@drawable/default_background"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="Title"/>
<TextView
android:id="@+id/acronym_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Acronym"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title_text"
style="@style/Widget.Leanback.DetailsDescriptionTitleStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginTop="15dp"
android:fontFamily="sans-serif-light"/>
<TextView
android:id="@+id/subtitle_text"
style="@style/Widget.Leanback.DetailsDescriptionSubtitleStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:fontFamily="sans-serif-condensed"/>
<TextView
android:id="@+id/speakers_text"
style="@style/Widget.Leanback.DetailsDescriptionSubtitleStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:fontFamily="sans-serif-condensed"/>
<TextView
android:id="@+id/description_text"
style="@style/Widget.Leanback.DetailsDescriptionBodyStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-regular"/>
</LinearLayout>

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
card_view:cardCornerRadius="4dp"
android:focusable="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="64dp"
android:layout_height="64dp"
app:srcCompat="@drawable/default_background"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="Title"/>
<TextView
android:id="@+id/acronym_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Acronym"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:name="de.nicidienase.chaosflix.touch.fragments.ConferenceFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="4dp"
app:layoutManager="LinearLayoutManager"
tools:context="de.nicidienase.chaosflix.touch.fragments.ConferenceFragment"
tools:listitem="@layout/fragment_conference"/>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/header_icon"
android:layout_width="32dp"
android:layout_height="32dp"/>
<TextView
android:id="@+id/header_label"
android:layout_marginTop="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"/>
</LinearLayout>

View file

@ -1,18 +0,0 @@
<resources>
<color name="background_gradient_start">#000000</color>
<color name="background_gradient_end">#DDDDDD</color>
<color name="fastlane_background">#0096a6</color>
<color name="search_opaque">#ffaa3f</color>
<color name="selected_background">#ffaa3f</color>
<color name="detail_background">#0096a6</color>
<color name="soft_opaque">#30000000</color>
<color name="img_soft_opaque">#30FF0000</color>
<color name="img_full_opaque">#00000000</color>
<color name="black_opaque">#AA000000</color>
<color name="black">#59000000</color>
<color name="white">#FFFFFF</color>
<color name="orange_transparent">#AAFADCA7</color>
<color name="orange">#FADCA7</color>
<color name="yellow">#EEFF41</color>
<color name="default_background">#3d3d3d</color>
</resources>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="detail_thumb_width">274dp</dimen>
<dimen name="detail_thumb_height">274dp</dimen>
<dimen name="spinner_width">50dp</dimen>
<dimen name="spinner_height">50dp</dimen>
</resources>

View file

@ -1,40 +0,0 @@
<resources>
<string name="api_media_ccc_url">https://api.media.ccc.de</string>
<string name="streaming_media_ccc_url">https://streaming.media.ccc.de</string>
<!--<string name="streaming_media_ccc_url">http://192.168.178.98:8000</string>-->
<string name="app_name">Chaosflix</string>
<string name="related_movies">Related Videos</string>
<string name="error">Error</string>
<string name="ok">OK</string>
<string name="pause">Pause</string>
<string name="play">Play</string>
<string name="stop">Stop</string>
<string name="video_error_media_load_timeout">Media loading timed out</string>
<string name="video_error_server_inaccessible">Media server was not reachable</string>
<string name="video_error_unknown_error">Failed to load video</string>
<string name="error_fragment_message">An error occurred</string>
<string name="dismiss_error">Dismiss</string>
<string name="oops">Oops</string>
<string name="random_talks_on_this_track">Random Talks in the same Track</string>
<string name="random_talks">Other random Talks at this Conference</string>
<string name="resume_question">Resume previous position or start from beginning</string>
<string name="start_again">Start again</string>
<string name="resume">Resume</string>
<string name="related_talks">Related Talks</string>
<string name="recomendations">Recomendations</string>
<string name="watchlist">Watchlist</string>
<string name="recordings">Recordings</string>
<string name="streaming_prefix">[streaming]</string>
<string name="conferences">Conferences</string>
<string name="remove_from_watchlist">Remove from Watchlist</string>
<string name="add_to_watchlist">Add to Watchlist</string>
<string name="streams">Streams</string>
<string name="livestreams">Livestreams</string>
<string name="watchlist_message">You can find you watchlist on the Homescreen of Chaosflix</string>
<string name="watchlist_preferences_key">watchlist</string>
<string name="watchlist_dialog_needed">new-watchlist</string>
<string name="return_to_homescreen">Return to Homescreen</string>
</resources>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@style/Theme.Leanback">
</style>
</resources>

View file

@ -1,14 +0,0 @@
<resources>
<style name="Theme.Example.Leanback" parent="Theme.Leanback">
<item name="android:windowEnterTransition">@android:transition/fade</item>
<item name="android:windowExitTransition">@android:transition/fade</item>
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowContentTransitions">true</item>
<item name="android:colorPrimary">@color/search_opaque</item>
</style>
</resources>

View file

@ -1,31 +0,0 @@
package de.nicidienase.chaosflix;
import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;
import com.orm.SugarContext;
/**
* Created by felix on 18.03.17.
*/
public class ChaosflixApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SugarContext.init(this);
}
@Override
public void onTerminate() {
SugarContext.terminate();
super.onTerminate();
}
@Override
protected void attachBaseContext(Context base) {
MultiDex.install(base);
super.attachBaseContext(base);
}
}

View file

49
common/build.gradle Normal file
View file

@ -0,0 +1,49 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.4.0'
compile 'com.github.satyan:sugar:1.4'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
compile 'joda-time:joda-time:2.9.9'
compile 'org.joda:joda-convert:1.8'
testImplementation 'junit:junit:4.12'
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}

21
common/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,2 @@
<manifest package="de.nicidienase.chaosflix"
xmlns:android="http://schemas.android.com/apk/res/android"/>

View file

@ -74,8 +74,8 @@ public class ConferencesWrapper {
public static List<String> getOrderedConferencesList() {
return Arrays.asList("congress", "sendezentrum", "camp",
"broadcast/chaosradio", "eh", "gpn",
"froscon", "mrmcd", "sigint",
"gpn", "mrmcd", "broadcast/chaosradio",
"eh", "froscon", "sigint",
"datenspuren", "fiffkon", "cryptocon");
}

View file

@ -0,0 +1,5 @@
<resources>
<string name="app_name">Chaosflix-Common</string>
<string name="api_media_ccc_url">https://api.media.ccc.de</string>
<string name="streaming_media_ccc_url">https://streaming.media.ccc.de</string>
</resources>

Some files were not shown because too many files have changed in this diff Show more