Touch: refactor eventDetails

This commit is contained in:
Felix 2019-04-29 00:49:16 +02:00
parent 14cc7686e5
commit 88c1c89be5
14 changed files with 310 additions and 200 deletions

View file

@ -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()
}
}

View file

@ -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)

View file

@ -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"
}
}

View file

@ -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>

View file

@ -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"

View file

@ -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}")

View file

@ -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

View file

@ -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());

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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>

View file

@ -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"/>

View file

@ -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>