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.google.android.exoplayer:exoplayer:r2.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-okhttp3:1.4.2'

View file

@ -2,6 +2,7 @@ package de.nicidienase.chaosflix.touch.playback;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@ -9,15 +10,12 @@ import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.android.exoplayer2.C;
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.trackselection.AdaptiveTrackSelection;
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.DefaultBandwidthMeter;
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.util.Util;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.nicidienase.chaosflix.R;
import de.nicidienase.chaosflix.common.entities.recording.persistence.PersistentEvent;
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;
public class ExoPlayerFragment extends Fragment implements PlayerEventListener.PlayerStateChangeListener {
private static final String TAG = ExoPlayerFragment.class.getSimpleName();
public static final String PLAYBACK_STATE = "playback_state";
private static final String ARG_EVENT = "event";
private static final String ARG_RECORDING = "recording";
private static final String TAG = ExoPlayerFragment.class.getSimpleName();
private static final String PLAYBACK_STATE = "playback_state";
private static final String ARG_item = "item";
private OnMediaPlayerInteractionListener listener;
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 Handler mainHandler = new Handler();
private Handler mainHandler = new Handler();
private boolean playbackState = true;
private PersistentEvent event;
private PersistentRecording recording;
private SimpleExoPlayer exoPlayer;
private PlayerViewModel viewModel;
private SimpleExoPlayer exoPlayer;
private PlayerViewModel viewModel;
private PlaybackItem item;
FragmentExoPlayerBinding binding;
ExoPlaybackControlsoverlayBinding overlayBinding;
public ExoPlayerFragment() {
}
public static ExoPlayerFragment newInstance(PersistentEvent event, PersistentRecording recording) {
public static ExoPlayerFragment newInstance(PlaybackItem item) {
ExoPlayerFragment fragment = new ExoPlayerFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_EVENT, event);
args.putParcelable(ARG_RECORDING, recording);
args.putParcelable(ARG_item, item);
fragment.setArguments(args);
return fragment;
}
@ -96,8 +81,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
event = getArguments().getParcelable(ARG_EVENT);
recording = getArguments().getParcelable(ARG_RECORDING);
item = getArguments().getParcelable(ARG_item);
}
if (savedInstanceState != null) {
playbackState = savedInstanceState.getBoolean(PLAYBACK_STATE, true);
@ -107,17 +91,20 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
@Override
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
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
if (toolbar != null) {
toolbar.setTitle(event.getTitle());
toolbar.setSubtitle(event.getSubtitle());
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
if (overlayBinding != null) {
((AppCompatActivity) getActivity()).setSupportActionBar(overlayBinding.toolbar);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
if (exoPlayer == null) {
@ -130,12 +117,12 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
super.onResume();
if (exoPlayer != null) {
exoPlayer.setPlayWhenReady(playbackState);
viewModel.getPlaybackProgress(event.getEventId()).observe(this, playbackProgress -> {
viewModel.getPlaybackProgress(item.getEventId()).observe(this, playbackProgress -> {
if (playbackProgress != null) {
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() {
super.onPause();
if (exoPlayer != null) {
viewModel.setPlaybackProgress(event, exoPlayer.getCurrentPosition());
viewModel.setPlaybackProgress(item.getEventId(), exoPlayer.getCurrentPosition());
exoPlayer.setPlayWhenReady(false);
}
}
@ -172,7 +159,7 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
private SimpleExoPlayer setupPlayer() {
Log.d(TAG, "Setting up Player.");
videoView.setKeepScreenOn(true);
binding.videoView.setKeepScreenOn(true);
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.prepare(buildMediaSource(Uri.parse(recording.getRecordingUrl()), ""));
exoPlayer.prepare(buildMediaSource(Uri.parse(item.getUrl()), ""));
return exoPlayer;
}
@ -211,29 +198,30 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
@Override
public void notifyLoadingStart() {
if (progressBar != null) {
progressBar.setVisibility(View.VISIBLE);
if (binding.progressBar != null) {
binding.progressBar.setVisibility(View.VISIBLE);
}
}
@Override
public void notifyLoadingFinished() {
if (progressBar != null) {
progressBar.setVisibility(View.INVISIBLE);
if (binding.progressBar != null) {
binding.progressBar.setVisibility(View.INVISIBLE);
}
}
@Override
public void notifyError(String errorMessage) {
Snackbar.make(videoView, errorMessage, Snackbar.LENGTH_LONG).show();
Snackbar.make(binding.videoView, errorMessage, Snackbar.LENGTH_LONG).show();
}
@Override
public void notifyEnd() {
viewModel.deletePlaybackProgress(event);
viewModel.deletePlaybackProgress(item.getEventId());
}
public interface OnMediaPlayerInteractionListener {}
public interface OnMediaPlayerInteractionListener {
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
DataSource.Factory mediaDataSourceFactory = buildDataSourceFactory(true);
@ -263,9 +251,9 @@ public class ExoPlayerFragment extends Fragment implements PlayerEventListener.P
private HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
return new DefaultHttpDataSourceFactory(userAgent,
bandwidthMeter,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true);
bandwidthMeter,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
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) {
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.commit()
}

View file

@ -3,7 +3,6 @@ package de.nicidienase.chaosflix.touch.playback
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.ViewModel
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.network.RecordingService
import de.nicidienase.chaosflix.common.network.StreamingService
@ -13,18 +12,18 @@ import io.reactivex.schedulers.Schedulers
internal class PlayerViewModel(val database: ChaosflixDatabase,
val recordingApi: RecordingService,
val streamingApi: StreamingService) : ViewModel() {
fun getPlaybackProgress(apiID: Long): LiveData<PlaybackProgress>
= database.playbackProgressDao().getProgressForEvent(apiID)
fun getPlaybackProgress(apiID: Long): LiveData<PlaybackProgress>
= database.playbackProgressDao().getProgressForEvent(apiID)
fun setPlaybackProgress(event: PersistentEvent, progress: Long) {
Single.fromCallable {
database.playbackProgressDao().saveProgress(PlaybackProgress(event.eventId, progress))
}.subscribeOn(Schedulers.io()).subscribe()
}
fun setPlaybackProgress(eventId: Long, progress: Long) {
Single.fromCallable {
database.playbackProgressDao().saveProgress(PlaybackProgress(eventId, progress))
}.subscribeOn(Schedulers.io()).subscribe()
}
fun deletePlaybackProgress(event: PersistentEvent) {
Single.fromCallable {
database.playbackProgressDao().deleteItem(event.eventId)
}
}
fun deletePlaybackProgress(eventId: Long) {
Single.fromCallable {
database.playbackProgressDao().deleteItem(eventId)
}
}
}

View file

@ -1,101 +1,112 @@
<?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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<layout
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/player_controls_background"
android:layout_alignParentTop="true"
android:orientation="vertical">
<data>
<variable
name="item"
type="de.nicidienase.chaosflix.touch.playback.PlaybackItem"/>
</data>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minHeight="?attr/actionBarSize"
style="@style/ToolbarStyle"
app:title="Title"
app:titleTextColor="@color/white"
app:subtitle="subtitle"
app:subtitleTextColor="@color/white"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@drawable/player_controls_background"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/player_controls_background"
android:gravity="center_horizontal|center_vertical"
android:orientation="horizontal">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/ToolbarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minHeight="?attr/actionBarSize"
app:subtitle="@{item.subtitle}"
app:subtitleTextColor="@color/white"
app:title="@{item.title}"
app:titleTextColor="@color/white"/>
<ImageButton
android:id="@id/exo_play"
android:scaleX="2"
android:scaleY="2"
style="@style/ExoMediaButton.Play"/>
</LinearLayout>
<ImageButton
android:id="@id/exo_pause"
android:scaleX="2"
android:scaleY="2"
style="@style/ExoMediaButton.Pause"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/player_controls_background"
android:gravity="center_horizontal|center_vertical"
android:orientation="horizontal">
</LinearLayout>
<ImageButton
android:id="@id/exo_play"
style="@style/ExoMediaButton.Play"
android:scaleX="2"
android:scaleY="2"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@drawable/player_controls_background"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingTop="16dp">
<ImageButton
android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"
android:scaleX="2"
android:scaleY="2"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="horizontal">
</LinearLayout>
<TextView
android:id="@+id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:06:29"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@drawable/player_controls_background"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingTop="16dp">
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="horizontal">
<TextView
android:id="@+id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:23:49"/>
<TextView
android:id="@+id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:06:29"/>
</LinearLayout>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:id="@+id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
tools:ignore="RtlHardcoded"
tools:text="1:23:49"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</layout>

View file

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