replace last usage of butterknife with databinding and add Playback-Item to decouple player from Entities

This commit is contained in:
Felix 2017-11-23 23:54:40 +01:00
parent c2c39e8c4d
commit 27d53e8081
7 changed files with 219 additions and 174 deletions

View file

@ -81,8 +81,6 @@ dependencies {
implementation 'com.github.medyo:android-about-page:1.2.2' implementation 'com.github.medyo:android-about-page:1.2.2'
implementation 'com.google.android.exoplayer:exoplayer:r2.5.2' implementation 'com.google.android.exoplayer:exoplayer:r2.5.2'
implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton:butterknife:8.5.1'
kapt 'com.jakewharton:butterknife-compiler:8.5.1'
debugImplementation 'com.facebook.stetho:stetho:1.4.2' debugImplementation 'com.facebook.stetho:stetho:1.4.2'
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.4.2' debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.4.2'

View file

@ -2,6 +2,7 @@ package de.nicidienase.chaosflix.touch.playback;
import android.arch.lifecycle.ViewModelProviders; import android.arch.lifecycle.ViewModelProviders;
import android.content.Context; import android.content.Context;
import android.databinding.DataBindingUtil;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -9,15 +10,12 @@ import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultLoadControl;
@ -35,7 +33,6 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
@ -44,50 +41,38 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.nicidienase.chaosflix.R; import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentEvent; import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentEvent;
import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentRecording; import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentRecording;
import de.nicidienase.chaosflix.databinding.ExoPlaybackControlsoverlayBinding;
import de.nicidienase.chaosflix.databinding.FragmentExoPlayerBinding;
import de.nicidienase.chaosflix.touch.ViewModelFactory; import de.nicidienase.chaosflix.touch.ViewModelFactory;
public class ExoPlayerFragment extends Fragment implements PlayerEventListener.PlayerStateChangeListener { public class ExoPlayerFragment extends Fragment implements PlayerEventListener.PlayerStateChangeListener {
private static final String TAG = ExoPlayerFragment.class.getSimpleName(); private static final String TAG = ExoPlayerFragment.class.getSimpleName();
public static final String PLAYBACK_STATE = "playback_state"; private static final String PLAYBACK_STATE = "playback_state";
private static final String ARG_EVENT = "event"; private static final String ARG_item = "item";
private static final String ARG_RECORDING = "recording";
private OnMediaPlayerInteractionListener listener; private OnMediaPlayerInteractionListener listener;
private final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
@BindView(R.id.video_view)
SimpleExoPlayerView videoView;
@BindView(R.id.progressBar)
ProgressBar progressBar;
@Nullable
@BindView(R.id.toolbar)
Toolbar toolbar;
@Nullable
@BindView(R.id.subtitle_text)
TextView subtitleText;
private String userAgent; private String userAgent;
private Handler mainHandler = new Handler(); private Handler mainHandler = new Handler();
private boolean playbackState = true; private boolean playbackState = true;
private PersistentEvent event;
private PersistentRecording recording;
private SimpleExoPlayer exoPlayer; private SimpleExoPlayer exoPlayer;
private PlayerViewModel viewModel; private PlayerViewModel viewModel;
private PlaybackItem item;
FragmentExoPlayerBinding binding;
ExoPlaybackControlsoverlayBinding overlayBinding;
public ExoPlayerFragment() { public ExoPlayerFragment() {
} }
public static ExoPlayerFragment newInstance(PersistentEvent event, PersistentRecording recording) { public static ExoPlayerFragment newInstance(PlaybackItem item) {
ExoPlayerFragment fragment = new ExoPlayerFragment(); ExoPlayerFragment fragment = new ExoPlayerFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ARG_EVENT, event); args.putParcelable(ARG_item, item);
args.putParcelable(ARG_RECORDING, recording);
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
} }
@ -96,8 +81,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getArguments() != null) { if (getArguments() != null) {
event = getArguments().getParcelable(ARG_EVENT); item = getArguments().getParcelable(ARG_item);
recording = getArguments().getParcelable(ARG_RECORDING);
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
playbackState = savedInstanceState.getBoolean(PLAYBACK_STATE, true); playbackState = savedInstanceState.getBoolean(PLAYBACK_STATE, true);
@ -107,17 +91,20 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_exo_player, container, false); binding = DataBindingUtil.inflate(inflater, R.layout.fragment_exo_player, container, false);
// overlayBinding = DataBindingUtil.findBinding(binding.videoView.getOverlayFrameLayout());
overlayBinding = DataBindingUtil.inflate(inflater, R.layout.exo_playback_controlsoverlay, null, false);
overlayBinding.setItem(item);
// binding.videoView.getOverlayFrameLayout().removeAllViews();
// binding.videoView.getOverlayFrameLayout().addView(overlayBinding.getRoot());
return binding.getRoot();
} }
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view); if (overlayBinding != null) {
if (toolbar != null) { ((AppCompatActivity) getActivity()).setSupportActionBar(overlayBinding.toolbar);
toolbar.setTitle(event.getTitle());
toolbar.setSubtitle(event.getSubtitle());
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
if (exoPlayer == null) { if (exoPlayer == null) {
@ -130,12 +117,12 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
super.onResume(); super.onResume();
if (exoPlayer != null) { if (exoPlayer != null) {
exoPlayer.setPlayWhenReady(playbackState); exoPlayer.setPlayWhenReady(playbackState);
viewModel.getPlaybackProgress(event.getEventId()).observe(this, playbackProgress -> { viewModel.getPlaybackProgress(item.getEventId()).observe(this, playbackProgress -> {
if (playbackProgress != null) { if (playbackProgress != null) {
exoPlayer.seekTo(playbackProgress.getProgress()); exoPlayer.seekTo(playbackProgress.getProgress());
} }
}); });
videoView.setPlayer(exoPlayer); binding.videoView.setPlayer(exoPlayer);
} }
} }
@ -150,7 +137,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
if (exoPlayer != null) { if (exoPlayer != null) {
viewModel.setPlaybackProgress(event, exoPlayer.getCurrentPosition()); viewModel.setPlaybackProgress(item.getEventId(), exoPlayer.getCurrentPosition());
exoPlayer.setPlayWhenReady(false); exoPlayer.setPlayWhenReady(false);
} }
} }
@ -172,7 +159,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
private SimpleExoPlayer setupPlayer() { private SimpleExoPlayer setupPlayer() {
Log.d(TAG, "Setting up Player."); Log.d(TAG, "Setting up Player.");
videoView.setKeepScreenOn(true); binding.videoView.setKeepScreenOn(true);
userAgent = Util.getUserAgent(getContext(), getResources().getString(R.string.app_name)); userAgent = Util.getUserAgent(getContext(), getResources().getString(R.string.app_name));
@ -189,7 +176,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
exoPlayer.setPlayWhenReady(playbackState); exoPlayer.setPlayWhenReady(playbackState);
exoPlayer.prepare(buildMediaSource(Uri.parse(recording.getRecordingUrl()), "")); exoPlayer.prepare(buildMediaSource(Uri.parse(item.getUrl()), ""));
return exoPlayer; return exoPlayer;
} }
@ -211,29 +198,30 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
@Override @Override
public void notifyLoadingStart() { public void notifyLoadingStart() {
if (progressBar != null) { if (binding.progressBar != null) {
progressBar.setVisibility(View.VISIBLE); binding.progressBar.setVisibility(View.VISIBLE);
} }
} }
@Override @Override
public void notifyLoadingFinished() { public void notifyLoadingFinished() {
if (progressBar != null) { if (binding.progressBar != null) {
progressBar.setVisibility(View.INVISIBLE); binding.progressBar.setVisibility(View.INVISIBLE);
} }
} }
@Override @Override
public void notifyError(String errorMessage) { public void notifyError(String errorMessage) {
Snackbar.make(videoView, errorMessage, Snackbar.LENGTH_LONG).show(); Snackbar.make(binding.videoView, errorMessage, Snackbar.LENGTH_LONG).show();
} }
@Override @Override
public void notifyEnd() { public void notifyEnd() {
viewModel.deletePlaybackProgress(event); viewModel.deletePlaybackProgress(item.getEventId());
} }
public interface OnMediaPlayerInteractionListener {} public interface OnMediaPlayerInteractionListener {
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) { private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
DataSource.Factory mediaDataSourceFactory = buildDataSourceFactory(true); DataSource.Factory mediaDataSourceFactory = buildDataSourceFactory(true);

View file

@ -0,0 +1,36 @@
package de.nicidienase.chaosflix.touch.playback
import android.os.Parcel
import android.os.Parcelable
data class PlaybackItem (val title: String, val subtitle: String, val eventId: Long, val url: String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readLong(),
parcel.readString()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(title)
parcel.writeString(subtitle)
parcel.writeLong(eventId)
parcel.writeString(url)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<PlaybackItem> {
override fun createFromParcel(parcel: Parcel): PlaybackItem {
return PlaybackItem(parcel)
}
override fun newArray(size: Int): Array<PlaybackItem?> {
return arrayOfNulls(size)
}
}
}

View file

@ -25,7 +25,8 @@ class PlayerActivity : AppCompatActivity(), ExoPlayerFragment.OnMediaPlayerInter
if (savedInstanceState == null) { if (savedInstanceState == null) {
val ft = supportFragmentManager.beginTransaction() val ft = supportFragmentManager.beginTransaction()
val playerFragment = ExoPlayerFragment.newInstance(event, recording) val playbackItem = PlaybackItem(event.title, event.subtitle ?: "", event.eventId, recording.recordingUrl)
val playerFragment = ExoPlayerFragment.newInstance(playbackItem)
ft.replace(R.id.fragment_container, playerFragment) ft.replace(R.id.fragment_container, playerFragment)
ft.commit() ft.commit()
} }

View file

@ -3,7 +3,6 @@ package de.nicidienase.chaosflix.touch.playback
import android.arch.lifecycle.LiveData import android.arch.lifecycle.LiveData
import android.arch.lifecycle.ViewModel import android.arch.lifecycle.ViewModel
import de.nicidienase.chaosflix.common.entities.ChaosflixDatabase import de.nicidienase.chaosflix.common.entities.ChaosflixDatabase
import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentEvent
import de.nicidienase.chaosflix.common.entities.userdata.PlaybackProgress import de.nicidienase.chaosflix.common.entities.userdata.PlaybackProgress
import de.nicidienase.chaosflix.common.network.RecordingService import de.nicidienase.chaosflix.common.network.RecordingService
import de.nicidienase.chaosflix.common.network.StreamingService import de.nicidienase.chaosflix.common.network.StreamingService
@ -16,15 +15,15 @@ internal class PlayerViewModel(val database: ChaosflixDatabase,
fun getPlaybackProgress(apiID: Long): LiveData<PlaybackProgress> fun getPlaybackProgress(apiID: Long): LiveData<PlaybackProgress>
= database.playbackProgressDao().getProgressForEvent(apiID) = database.playbackProgressDao().getProgressForEvent(apiID)
fun setPlaybackProgress(event: PersistentEvent, progress: Long) { fun setPlaybackProgress(eventId: Long, progress: Long) {
Single.fromCallable { Single.fromCallable {
database.playbackProgressDao().saveProgress(PlaybackProgress(event.eventId, progress)) database.playbackProgressDao().saveProgress(PlaybackProgress(eventId, progress))
}.subscribeOn(Schedulers.io()).subscribe() }.subscribeOn(Schedulers.io()).subscribe()
} }
fun deletePlaybackProgress(event: PersistentEvent) { fun deletePlaybackProgress(eventId: Long) {
Single.fromCallable { Single.fromCallable {
database.playbackProgressDao().deleteItem(event.eventId) database.playbackProgressDao().deleteItem(eventId)
} }
} }
} }

View file

@ -1,7 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="de.nicidienase.chaosflix.touch.playback.PlaybackItem"/>
</data>
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -9,21 +18,21 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/player_controls_background"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:background="@drawable/player_controls_background"
android:orientation="vertical"> android:orientation="vertical">
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
style="@style/ToolbarStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:minHeight="?attr/actionBarSize" android:minHeight="?attr/actionBarSize"
style="@style/ToolbarStyle" app:subtitle="@{item.subtitle}"
app:title="Title" app:subtitleTextColor="@color/white"
app:titleTextColor="@color/white" app:title="@{item.title}"
app:subtitle="subtitle" app:titleTextColor="@color/white"/>
app:subtitleTextColor="@color/white"/>
</LinearLayout> </LinearLayout>
@ -37,15 +46,15 @@
<ImageButton <ImageButton
android:id="@id/exo_play" android:id="@id/exo_play"
style="@style/ExoMediaButton.Play"
android:scaleX="2" android:scaleX="2"
android:scaleY="2" android:scaleY="2"/>
style="@style/ExoMediaButton.Play"/>
<ImageButton <ImageButton
android:id="@id/exo_pause" android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"
android:scaleX="2" android:scaleX="2"
android:scaleY="2" android:scaleY="2"/>
style="@style/ExoMediaButton.Pause"/>
</LinearLayout> </LinearLayout>
@ -98,4 +107,6 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</layout>

View file

@ -1,22 +1,34 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout
xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:animateLayoutChanges="true"
android:background="@color/black" android:background="@color/black"
tools:context="de.nicidienase.chaosflix.touch.playback.ExoPlayerFragment"> tools:context="de.nicidienase.chaosflix.touch.playback.ExoPlayerFragment">
<com.google.android.exoplayer2.ui.SimpleExoPlayerView <com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/video_view" android:id="@+id/video_view"
android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/exo_playback_controlsoverlay"/> app:controller_layout_id="@layout/exo_playback_controlsoverlay"/>
<ProgressBar <FrameLayout
android:id="@+id/progressBar" android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"/> android:layout_gravity="center_vertical|center_horizontal">
</FrameLayout> <de.nicidienase.chaosflix.touch.ChaosflixLoadingSpinner
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/vector_loading_icon"
app:duration="2000"/>
</FrameLayout>
</FrameLayout>
</layout>