mirror of
https://github.com/NiciDieNase/chaosflix
synced 2024-11-26 22:20:24 +00:00
Touch: refactor eventDetails
This commit is contained in:
parent
14cc7686e5
commit
88c1c89be5
14 changed files with 310 additions and 200 deletions
|
@ -0,0 +1,29 @@
|
|||
package de.nicidienase.chaosflix.common
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
|
||||
fun Fragment.checkPermission(permission: String, requestCode: Int, action: ()->Unit){
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), permission)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(permission),
|
||||
requestCode)
|
||||
} else {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
fun AppCompatActivity.checkPermission(permission: String, requestCode: Int, action: ()->Unit){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& ContextCompat.checkSelfPermission(this, permission)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(permission),
|
||||
requestCode)
|
||||
} else {
|
||||
action()
|
||||
}
|
||||
}
|
|
@ -7,6 +7,10 @@ class PreferencesManager(val sharedPref: SharedPreferences) {
|
|||
private val keyMetered = "allow_metered_networks"
|
||||
private val keyAutoselectStream = "auto_select_stream"
|
||||
private val keyAutoselectRecording = "auto_select_recording"
|
||||
private val keyAlwaysUseExternalPlayer = "auto_external_player"
|
||||
|
||||
val externalPlayer: Boolean
|
||||
get() = sharedPref.getBoolean(keyAlwaysUseExternalPlayer, false)
|
||||
|
||||
fun getMetered() = sharedPref.getBoolean(keyMetered, false)
|
||||
|
||||
|
|
|
@ -17,15 +17,18 @@ import de.nicidienase.chaosflix.common.util.ThreadHandler
|
|||
import java.io.File
|
||||
|
||||
class DetailsViewModel(
|
||||
val database: ChaosflixDatabase,
|
||||
val offlineItemManager: OfflineItemManager,
|
||||
val preferencesManager: PreferencesManager,
|
||||
val downloader: Downloader
|
||||
private val database: ChaosflixDatabase,
|
||||
private val offlineItemManager: OfflineItemManager,
|
||||
private val preferencesManager: PreferencesManager,
|
||||
private val downloader: Downloader
|
||||
) : ViewModel() {
|
||||
|
||||
val state: SingleLiveEvent<LiveEvent<State,Bundle,String>>
|
||||
= SingleLiveEvent()
|
||||
|
||||
val autoselectRecording: Boolean
|
||||
get() = preferencesManager.getAutoselectRecording()
|
||||
|
||||
private val handler = ThreadHandler()
|
||||
|
||||
fun setEvent(event: Event): LiveData<Event?> {
|
||||
|
@ -81,6 +84,12 @@ class DetailsViewModel(
|
|||
return data
|
||||
}
|
||||
|
||||
fun relatedEventSelected(event: Event){
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(EVENT,event)
|
||||
state.postValue(LiveEvent(State.DisplayEvent, data = bundle))
|
||||
}
|
||||
|
||||
fun playEvent(event: Event) {
|
||||
handler.runOnBackgroundThread {
|
||||
|
||||
|
@ -94,36 +103,66 @@ class DetailsViewModel(
|
|||
}
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_LOCAL_PATH, offlineEvent.localPath)
|
||||
bundle.putParcelable(KEY_PLAY_RECORDING, recording)
|
||||
state.postValue(LiveEvent(State.PlayOfflineItem, data = bundle))
|
||||
bundle.putParcelable(RECORDING, recording)
|
||||
bundle.putParcelable(EVENT, event)
|
||||
if(preferencesManager.externalPlayer){
|
||||
state.postValue(LiveEvent(State.PlayExternal,bundle))
|
||||
} else {
|
||||
state.postValue(LiveEvent(State.PlayOfflineItem, data = bundle))
|
||||
}
|
||||
} else {
|
||||
// select quality then playEvent
|
||||
val items = database.recordingDao().findRecordingByEventSync(event.id).toTypedArray()
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(EVENT,event)
|
||||
bundle.putParcelableArray(KEY_SELECT_RECORDINGS, items)
|
||||
state.postValue(LiveEvent(State.SelectRecording, data = bundle ))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun playRecording(recording: Recording){
|
||||
fun playRecording(event: Event, recording: Recording){
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(KEY_PLAY_RECORDING, recording)
|
||||
state.postValue(LiveEvent(State.PlayOnlineItem, data = bundle))
|
||||
bundle.putParcelable(RECORDING, recording)
|
||||
bundle.putParcelable(EVENT,event)
|
||||
if(preferencesManager.externalPlayer){
|
||||
state.postValue(LiveEvent(State.PlayExternal, bundle))
|
||||
} else {
|
||||
state.postValue(LiveEvent(State.PlayOnlineItem, bundle))
|
||||
}
|
||||
}
|
||||
|
||||
fun getAutoselectRecording() = preferencesManager.getAutoselectRecording()
|
||||
fun downloadRecordingForEvent(event: Event)
|
||||
= postStateWithEventAndRecordings(State.DownloadRecording, event)
|
||||
|
||||
fun playInExternalPlayer(event: Event) = postStateWithEventAndRecordings(State.PlayExternal, event)
|
||||
|
||||
private fun postStateWithEventAndRecordings(s: State, e: Event){
|
||||
handler.runOnBackgroundThread {
|
||||
val items = database.recordingDao().findRecordingByEventSync(e.id).toTypedArray()
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(EVENT, e)
|
||||
bundle.putParcelableArray(KEY_SELECT_RECORDINGS, items)
|
||||
state.postValue(LiveEvent(s, bundle))
|
||||
}
|
||||
}
|
||||
|
||||
enum class State{
|
||||
PlayOfflineItem, PlayOnlineItem, SelectRecording, Error
|
||||
PlayOfflineItem,
|
||||
PlayOnlineItem,
|
||||
SelectRecording,
|
||||
DownloadRecording,
|
||||
DisplayEvent,
|
||||
PlayExternal,
|
||||
Error
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = DetailsViewModel::class.simpleName
|
||||
val KEY_LOCAL_PATH = "local_path"
|
||||
val KEY_SELECT_RECORDINGS = "select_recordings"
|
||||
val KEY_PLAY_RECORDING = "play_recording"
|
||||
const val KEY_LOCAL_PATH = "local_path"
|
||||
const val KEY_SELECT_RECORDINGS = "select_recordings"
|
||||
const val RECORDING = "recording"
|
||||
const val EVENT = "event"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<string name="pref_autoselect_recording">Don\'t show selection dialog, just use HD-mp4</string>
|
||||
<string name="pref_autoselect_stream">Don\'t show selection dialog, just use DASH</string>
|
||||
<string name="pref_mobile_downloads">Enable downloads over mobile or payed networks</string>
|
||||
<string name="pref_external_player">Always use external player</string>
|
||||
<string name="setting_choose_stream">Automatically choose stream</string>
|
||||
<string name="setting_metered_networks">Allow downloads over metered networks</string>
|
||||
<string name="settings_choose_recording">Automatically choose recording</string>
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
android:summary="@string/pref_autoselect_recording"
|
||||
android:title="@string/settings_choose_recording"/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="auto_external_player"
|
||||
android:title="@string/pref_external_player"
|
||||
android:summary=""/>
|
||||
|
||||
|
||||
<Preference
|
||||
android:id="@+id/download_folder"
|
||||
android:key="download_folder"
|
||||
|
|
|
@ -7,24 +7,26 @@ import android.view.ViewGroup
|
|||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.Event
|
||||
import de.nicidienase.chaosflix.touch.OnEventSelectedListener
|
||||
import de.nicidienase.chaosflix.touch.databinding.ItemEventCardviewBinding
|
||||
import de.nicidienase.chaosflix.touch.databinding.RelatedEventCardviewLayoutBinding
|
||||
import java.util.*
|
||||
|
||||
open class EventRecyclerViewAdapter(val listener: OnEventSelectedListener) :
|
||||
open class EventRecyclerViewAdapter(val listener: (Event)->Unit) :
|
||||
ItemRecyclerViewAdapter<Event, EventRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
override fun getComparator(): Comparator<in Event>? {
|
||||
return Comparator { o1, o2 -> o1.title.compareTo(o2.title) }
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return items.get(position).id
|
||||
return items[position].id
|
||||
}
|
||||
|
||||
override fun getFilteredProperties(item: Event): List<String> {
|
||||
return listOf(item.title,
|
||||
return listOfNotNull(item.title,
|
||||
item.subtitle,
|
||||
item.description,
|
||||
item.getSpeakerString()
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(p0: ViewGroup, pItemConferenceCardviewBinding1: Int): ViewHolder {
|
||||
|
@ -36,7 +38,7 @@ open class EventRecyclerViewAdapter(val listener: OnEventSelectedListener) :
|
|||
val event = items[position]
|
||||
holder.binding.event = event
|
||||
holder.binding.root.setOnClickListener {
|
||||
listener.onEventSelected(event)
|
||||
listener(event)
|
||||
}
|
||||
|
||||
ViewCompat.setTransitionName(holder.binding.titleText, "title_${event.guid}")
|
||||
|
|
|
@ -24,11 +24,11 @@ abstract class ItemRecyclerViewAdapter<T,VH : RecyclerView.ViewHolder?>()
|
|||
return _filter
|
||||
}
|
||||
|
||||
private var _items: MutableList<T> = ArrayList<T>()
|
||||
private var _items: List<T> = listOf()
|
||||
|
||||
private var filteredItems: MutableList<T> = _items
|
||||
private var filteredItems: List<T> = _items
|
||||
|
||||
var items: MutableList<T>
|
||||
var items: List<T>
|
||||
get() = filteredItems
|
||||
set(value) {
|
||||
_items = value
|
||||
|
@ -39,16 +39,16 @@ abstract class ItemRecyclerViewAdapter<T,VH : RecyclerView.ViewHolder?>()
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun addItem(item: T) {
|
||||
if (items.contains(item)) {
|
||||
val index = items.indexOf(item)
|
||||
items[index] = item
|
||||
notifyItemChanged(index)
|
||||
} else {
|
||||
items.add(item)
|
||||
notifyItemInserted(items.size - 1)
|
||||
}
|
||||
}
|
||||
// fun addItem(item: T) {
|
||||
// if (items.contains(item)) {
|
||||
// val index = items.indexOf(item)
|
||||
// items[index] = item
|
||||
// notifyItemChanged(index)
|
||||
// } else {
|
||||
// items.add(item)
|
||||
// notifyItemInserted(items.size - 1)
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return filteredItems.size
|
||||
|
@ -58,7 +58,7 @@ abstract class ItemRecyclerViewAdapter<T,VH : RecyclerView.ViewHolder?>()
|
|||
override fun performFiltering(filterText: CharSequence?): FilterResults {
|
||||
val filterResults = FilterResults()
|
||||
filterText?.let { text: CharSequence ->
|
||||
if (text.length > 0) {
|
||||
if (text.isNotEmpty()) {
|
||||
val list = _items.filter { getFilteredProperties(it).any { it.contains(text, true) } }
|
||||
filterResults.values = list
|
||||
filterResults.count = list.size
|
||||
|
|
|
@ -30,6 +30,7 @@ import de.nicidienase.chaosflix.touch.OnEventSelectedListener;
|
|||
import de.nicidienase.chaosflix.touch.browse.BrowseFragment;
|
||||
import de.nicidienase.chaosflix.touch.browse.adapters.EventRecyclerViewAdapter;
|
||||
import de.nicidienase.chaosflix.touch.databinding.FragmentEventsListBinding;
|
||||
import kotlin.Unit;
|
||||
|
||||
public class EventsListFragment extends BrowseFragment implements SearchView.OnQueryTextListener {
|
||||
|
||||
|
@ -99,7 +100,10 @@ public class EventsListFragment extends BrowseFragment implements SearchView.OnQ
|
|||
}
|
||||
binding.list.setLayoutManager(layoutManager);
|
||||
|
||||
eventAdapter = new EventRecyclerViewAdapter(listener);
|
||||
eventAdapter = new EventRecyclerViewAdapter(event -> {
|
||||
listener.onEventSelected(event);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
eventAdapter.setHasStableIds(true);
|
||||
binding.list.setAdapter(eventAdapter);
|
||||
DividerItemDecoration itemDecoration = new DividerItemDecoration(binding.list.getContext(), layoutManager.getOrientation());
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package de.nicidienase.chaosflix.touch.eventdetails
|
||||
|
||||
import android.Manifest
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import de.nicidienase.chaosflix.common.ChaosflixUtil
|
||||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.Event
|
||||
import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.Recording
|
||||
import de.nicidienase.chaosflix.common.viewmodel.DetailsViewModel
|
||||
|
@ -18,6 +20,7 @@ import de.nicidienase.chaosflix.touch.OnEventSelectedListener
|
|||
import de.nicidienase.chaosflix.touch.R
|
||||
import de.nicidienase.chaosflix.touch.browse.cast.CastService
|
||||
import de.nicidienase.chaosflix.touch.playback.PlayerActivity
|
||||
import kotlinx.android.synthetic.main.activity_eventdetails.*
|
||||
|
||||
class EventDetailsActivity : AppCompatActivity(),
|
||||
EventDetailsFragment.OnEventDetailsFragmentInteractionListener,
|
||||
|
@ -26,6 +29,8 @@ class EventDetailsActivity : AppCompatActivity(),
|
|||
|
||||
private lateinit var castService: CastService
|
||||
|
||||
private var selectDialog: AlertDialog? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_eventdetails)
|
||||
|
@ -34,11 +39,97 @@ class EventDetailsActivity : AppCompatActivity(),
|
|||
|
||||
viewModel = ViewModelProviders.of(this, ViewModelFactory(this)).get(DetailsViewModel::class.java)
|
||||
|
||||
viewModel.state.observe(this, Observer { liveEvent ->
|
||||
if(liveEvent == null){
|
||||
return@Observer
|
||||
}
|
||||
val localFile = liveEvent.data?.getString(DetailsViewModel.KEY_LOCAL_PATH)
|
||||
val recording = liveEvent.data?.getParcelable<Recording>(DetailsViewModel.RECORDING)
|
||||
val event = liveEvent.data?.getParcelable<Event>(DetailsViewModel.EVENT)
|
||||
val selectItems: Array<Recording>? = liveEvent.data?.getParcelableArray(DetailsViewModel.KEY_SELECT_RECORDINGS) as Array<Recording>?
|
||||
when(liveEvent.state){
|
||||
DetailsViewModel.State.DisplayEvent -> {
|
||||
if (event != null) {
|
||||
showFragmentForEvent(event,true)
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.PlayOfflineItem -> {
|
||||
if(event != null && recording != null){
|
||||
playItem(event, recording, localFile)
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.PlayOnlineItem -> {
|
||||
if(event != null && recording != null){
|
||||
playItem(event,recording)
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.SelectRecording -> {
|
||||
if(event != null && selectItems != null) {
|
||||
selectRecording(event,selectItems.asList()) {e,r ->
|
||||
viewModel.playRecording(e,r)
|
||||
}
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.DownloadRecording -> {
|
||||
if (event != null && selectItems != null) {
|
||||
selectRecording(event,selectItems.asList()) { e,r ->
|
||||
viewModel.download(e,r)
|
||||
}
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.PlayExternal -> {
|
||||
if (event != null) {
|
||||
if (selectItems != null) {
|
||||
selectRecording(event,selectItems.asList()) { _, r ->
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(r.recordingUrl)))
|
||||
}
|
||||
} else if (recording != null){
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(recording.recordingUrl)))
|
||||
}
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.Error -> {
|
||||
showSnackbar(liveEvent.error ?: "An Error occured")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val event = intent.getParcelableExtra<Event>(EXTRA_EVENT)
|
||||
|
||||
showFragmentForEvent(event)
|
||||
}
|
||||
|
||||
|
||||
private fun showSnackbar(message: String, duration: Int = Snackbar.LENGTH_LONG ){
|
||||
Snackbar.make(fragment_container, message, duration)
|
||||
}
|
||||
|
||||
private fun selectRecording(event: Event, recordings: List<Recording>, action: (Event, Recording) -> Unit) {
|
||||
val optimalRecording = ChaosflixUtil.getOptimalRecording(recordings)
|
||||
if (optimalRecording != null && viewModel.autoselectRecording) {
|
||||
action.invoke(event,optimalRecording)
|
||||
} else {
|
||||
val items: List<String> = recordings.map { getStringForRecording(it) }
|
||||
selectRecordingFromList(items, DialogInterface.OnClickListener { dialogInterface, i ->
|
||||
action.invoke(event, recordings[i])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStringForRecording(recording: Recording): String {
|
||||
return "${if (recording.isHighQuality) "HD" else "SD"} ${recording.folder} [${recording.language}]"
|
||||
}
|
||||
|
||||
private fun selectRecordingFromList(items: List<String>, resultHandler: DialogInterface.OnClickListener) {
|
||||
if (selectDialog != null) {
|
||||
selectDialog?.dismiss()
|
||||
}
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setItems(items.toTypedArray(), resultHandler)
|
||||
selectDialog = builder.create()
|
||||
selectDialog?.show()
|
||||
}
|
||||
|
||||
private fun showFragmentForEvent(event: Event, addToBackStack: Boolean = false) {
|
||||
val detailsFragment = EventDetailsFragment.newInstance(event)
|
||||
|
||||
|
@ -87,7 +178,7 @@ class EventDetailsActivity : AppCompatActivity(),
|
|||
|
||||
private val EXTRA_EVENT = "extra_event"
|
||||
private val EXTRA_URI = "extra_uri"
|
||||
private val TAG = EventDetailsActivity.javaClass.simpleName
|
||||
private val TAG = EventDetailsActivity::class.java.simpleName
|
||||
|
||||
fun launch(context: Context, event: Event) {
|
||||
val intent = Intent(context, EventDetailsActivity::class.java)
|
||||
|
|
|
@ -35,6 +35,7 @@ import de.nicidienase.chaosflix.common.viewmodel.DetailsViewModel
|
|||
import de.nicidienase.chaosflix.touch.databinding.FragmentEventDetailsBinding
|
||||
import de.nicidienase.chaosflix.touch.OnEventSelectedListener
|
||||
import de.nicidienase.chaosflix.common.viewmodel.ViewModelFactory
|
||||
import de.nicidienase.chaosflix.touch.browse.adapters.EventRecyclerViewAdapter
|
||||
|
||||
class EventDetailsFragment : Fragment() {
|
||||
|
||||
|
@ -44,13 +45,12 @@ class EventDetailsFragment : Fragment() {
|
|||
private lateinit var event: Event
|
||||
private var watchlistItem: WatchlistItem? = null
|
||||
private var eventSelectedListener: OnEventSelectedListener? = null
|
||||
private var selectDialog: AlertDialog? = null
|
||||
|
||||
private var layout: View? = null
|
||||
|
||||
private lateinit var viewModel: DetailsViewModel
|
||||
|
||||
private lateinit var relatedEventsAdapter: RelatedEventsRecyclerViewAdapter
|
||||
private lateinit var relatedEventsAdapter: EventRecyclerViewAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -87,15 +87,19 @@ class EventDetailsFragment : Fragment() {
|
|||
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
eventSelectedListener?.let { listener ->
|
||||
binding.relatedItemsList.apply {
|
||||
relatedEventsAdapter = RelatedEventsRecyclerViewAdapter(listener)
|
||||
adapter = relatedEventsAdapter
|
||||
val orientation = LinearLayoutManager.VERTICAL
|
||||
layoutManager = LinearLayoutManager(context, orientation, false)
|
||||
val itemDecoration = DividerItemDecoration(binding.relatedItemsList.context, orientation)
|
||||
addItemDecoration(itemDecoration)
|
||||
binding.relatedItemsList.apply {
|
||||
// relatedEventsAdapter = RelatedEventsRecyclerViewAdapter(listener)
|
||||
relatedEventsAdapter = EventRecyclerViewAdapter(){
|
||||
// viewModel.playEvent(it)
|
||||
viewModel.relatedEventSelected(it)
|
||||
}
|
||||
adapter = relatedEventsAdapter
|
||||
val orientation = LinearLayoutManager.VERTICAL
|
||||
layoutManager = LinearLayoutManager(context, orientation, false)
|
||||
val itemDecoration = DividerItemDecoration(binding.relatedItemsList.context, orientation)
|
||||
addItemDecoration(itemDecoration)
|
||||
}
|
||||
eventSelectedListener?.let { listener ->
|
||||
}
|
||||
|
||||
binding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
|
@ -122,40 +126,10 @@ class EventDetailsFragment : Fragment() {
|
|||
.load(event.thumbUrl)
|
||||
.apply(RequestOptions().fitCenter())
|
||||
.into(binding.thumbImage)
|
||||
|
||||
})
|
||||
viewModel.getRelatedEvents(event).observe(this, Observer {
|
||||
if(it != null){
|
||||
relatedEventsAdapter.events = it
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.state.observe(this, Observer { liveEvent ->
|
||||
if(liveEvent == null){
|
||||
return@Observer
|
||||
}
|
||||
when(liveEvent.state){
|
||||
DetailsViewModel.State.PlayOfflineItem -> {
|
||||
val localFile = liveEvent.data?.getString(DetailsViewModel.KEY_LOCAL_PATH)
|
||||
val recording = liveEvent.data?.getParcelable<Recording>(DetailsViewModel.KEY_PLAY_RECORDING)
|
||||
if(recording != null){
|
||||
listener?.playItem(event, recording, localFile)
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.PlayOnlineItem -> {
|
||||
liveEvent.data?.getParcelable<Recording>(DetailsViewModel.KEY_PLAY_RECORDING)?.let {
|
||||
listener?.playItem(event,it)
|
||||
}
|
||||
|
||||
}
|
||||
DetailsViewModel.State.SelectRecording -> {
|
||||
val selectItems: Array<Recording> =
|
||||
liveEvent.data?.getParcelableArray(DetailsViewModel.KEY_SELECT_RECORDINGS) as Array<Recording>
|
||||
selectRecording(selectItems.asList()) {
|
||||
viewModel.playRecording(it)
|
||||
}
|
||||
}
|
||||
DetailsViewModel.State.Error -> liveEvent.error?.let { showSnackbar(it) }
|
||||
relatedEventsAdapter.items = it
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -168,12 +142,6 @@ class EventDetailsFragment : Fragment() {
|
|||
})
|
||||
}
|
||||
|
||||
private fun showSnackbar(message: String, duration: Int = Snackbar.LENGTH_LONG ){
|
||||
view?.let {
|
||||
Snackbar.make(it, message, duration)
|
||||
}
|
||||
}
|
||||
|
||||
private fun play() {
|
||||
if(listener == null){
|
||||
return
|
||||
|
@ -181,34 +149,6 @@ class EventDetailsFragment : Fragment() {
|
|||
viewModel.playEvent(event)
|
||||
}
|
||||
|
||||
private fun selectRecording(recordings: List<Recording>, action: (recording: Recording) -> Unit) {
|
||||
val stream = ChaosflixUtil.getOptimalRecording(recordings)
|
||||
if (stream != null && viewModel.getAutoselectRecording()) {
|
||||
action.invoke(stream)
|
||||
} else {
|
||||
val items: List<String> = recordings.map { getStringForRecording(it) }
|
||||
selectRecordingFromList(items, DialogInterface.OnClickListener { dialogInterface, i ->
|
||||
action.invoke(recordings[i])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStringForRecording(recording: Recording): String {
|
||||
return "${if (recording.isHighQuality) "HD" else "SD"} ${recording.folder} [${recording.language}]"
|
||||
}
|
||||
|
||||
private fun selectRecordingFromList(items: List<String>, resultHandler: DialogInterface.OnClickListener) {
|
||||
this.context?.let { context ->
|
||||
if (selectDialog != null) {
|
||||
selectDialog?.dismiss()
|
||||
}
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder.setItems(items.toTypedArray(), resultHandler)
|
||||
selectDialog = builder.create()
|
||||
selectDialog?.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context?) {
|
||||
super.onAttach(context)
|
||||
if (context is OnEventSelectedListener) {
|
||||
|
@ -272,11 +212,7 @@ class EventDetailsFragment : Fragment() {
|
|||
return true
|
||||
}
|
||||
R.id.action_download -> {
|
||||
viewModel.getRecordingForEvent(event).observe(this, Observer { recordings ->
|
||||
if (recordings != null) {
|
||||
selectRecording(recordings, { recording -> downloadRecording(recording) })
|
||||
}
|
||||
})
|
||||
viewModel.downloadRecordingForEvent(event)
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_offline_item -> {
|
||||
|
@ -296,14 +232,7 @@ class EventDetailsFragment : Fragment() {
|
|||
return true
|
||||
}
|
||||
R.id.action_external_player -> {
|
||||
viewModel.getRecordingForEvent(event).observe(this, Observer { recordings ->
|
||||
if (recordings != null) {
|
||||
selectRecording(recordings) { recording ->
|
||||
val shareIntent = Intent(Intent.ACTION_VIEW, Uri.parse(recording.recordingUrl))
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
}
|
||||
})
|
||||
viewModel.playInExternalPlayer(event)
|
||||
return true
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
|
|
|
@ -9,9 +9,9 @@ import android.content.pm.PackageManager
|
|||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.preference.PreferenceFragmentCompat
|
||||
import de.nicidienase.chaosflix.R
|
||||
import de.nicidienase.chaosflix.common.checkPermission
|
||||
import de.nicidienase.chaosflix.common.viewmodel.PreferencesViewModel
|
||||
import de.nicidienase.chaosflix.common.viewmodel.ViewModelFactory
|
||||
import net.rdrei.android.dirchooser.DirectoryChooserActivity
|
||||
|
@ -151,16 +151,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
})
|
||||
}
|
||||
|
||||
private fun checkPermission(permission: String, requestCode: Int, action: ()->Unit){
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), permission)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(permission),
|
||||
requestCode)
|
||||
} else {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val PERMISSION_REQUEST_EXPORT_FAVORITES = 23
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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"
|
||||
xmlns:bind="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View"/>
|
||||
<variable
|
||||
name="event"
|
||||
type="de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.Event"/>
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
@ -18,89 +20,93 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="2dp"
|
||||
android:foreground="?android:attr/selectableItemBackground">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/linearLayout4"
|
||||
android:id="@+id/related_item_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
imageUrl="@{event.thumbUrl}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="100dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="center"
|
||||
bind:layout_constraintEnd_toEndOf="parent"
|
||||
bind:layout_constraintStart_toStartOf="parent"
|
||||
bind:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/cast_album_art_placeholder"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@color/transparent_black_70"
|
||||
android:padding="2dp"
|
||||
android:textColor="@color/white"
|
||||
bind:layout_constraintBottom_toBottomOf="@+id/imageView"
|
||||
bind:layout_constraintEnd_toEndOf="@+id/imageView"
|
||||
bind:time="@{event.length}"
|
||||
tools:text="1:23:45"/>
|
||||
|
||||
android:contentDescription="@string/titleimage"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/unknown"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1.777"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_text"
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@{event.title}"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
bind:layout_constraintEnd_toEndOf="parent"
|
||||
bind:layout_constraintHorizontal_bias="0.0"
|
||||
bind:layout_constraintStart_toStartOf="parent"
|
||||
bind:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:text="Title"/>
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="This is a very long title text that propably won't fit in one line, maybe even need three."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@{event.subtitle}"
|
||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
|
||||
bind:layout_constraintBottom_toBottomOf="parent"
|
||||
bind:layout_constraintEnd_toStartOf="@+id/tag_text"
|
||||
bind:layout_constraintHorizontal_bias="0.0"
|
||||
bind:layout_constraintStart_toStartOf="@+id/title_text"
|
||||
bind:layout_constraintTop_toBottomOf="@+id/title_text"
|
||||
tools:text="Subtitle"/>
|
||||
android:visibility="@{event.subtitle.length() == 0 ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tag_text"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_text"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:text="subtitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tag_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="bottom|end"
|
||||
android:text=""
|
||||
bind:layout_constraintBottom_toBottomOf="parent"
|
||||
bind:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="Tag"/>
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/subtitle_text"
|
||||
tools:text="TAGS"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@color/transparent_black_70"
|
||||
android:padding="2dp"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imageView"
|
||||
bind:time="@{event.length}"
|
||||
tools:text="1:23:45"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:bind="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View"/>
|
||||
<variable
|
||||
name="event"
|
||||
type="de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.Event"/>
|
||||
|
@ -30,15 +30,19 @@
|
|||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
imageUrl="@{event.thumbUrl}"
|
||||
android:layout_width="@dimen/thumbnail_width"
|
||||
android:layout_height="0dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/titleimage"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/unknown"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1.7777"
|
||||
app:layout_constraintDimensionRatio="1.777"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_text"
|
||||
|
@ -47,29 +51,34 @@
|
|||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@{event.title}"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title"/>
|
||||
tools:text="This is a very long title text that propably won't fit in one line, maybe even need three."/>
|
||||
<!--android:ellipsize="end"-->
|
||||
<!--android:maxLines="2"-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@{event.subtitle}"
|
||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
|
||||
android:visibility="@{event.subtitle.length() == 0 ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tag_text"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_text"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:text="subtitle"/>
|
||||
<!--android:ellipsize="end"-->
|
||||
<!--android:maxLines="2"-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tag_text"
|
||||
|
@ -79,6 +88,7 @@
|
|||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="bottom|end"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/subtitle_text"
|
||||
|
@ -88,16 +98,13 @@
|
|||
android:id="@+id/duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@color/transparent_black_70"
|
||||
android:padding="2dp"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imageView"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
bind:time="@{event.length}"
|
||||
tools:text="1:23:45"/>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<resources>
|
||||
<dimen name="thumbnail_width">120dp</dimen>
|
||||
<dimen name="thumbnail_height">68dp</dimen>
|
||||
<dimen name="related_event_height">90dp</dimen>
|
||||
<dimen name="appbar_header_height">256dp</dimen>
|
||||
<dimen name="margin_content">120dp</dimen>
|
||||
<dimen name="details_text_margin">8dp</dimen>
|
||||
|
|
Loading…
Reference in a new issue