Skip to content
This repository was archived by the owner on Feb 17, 2020. It is now read-only.

Commit 432ea01

Browse files
authored
Merge pull request #560 from squanchy-dev/allow_workshop_favourites
Convert EventDetails to ConstraintLayout, fix timezones, allow workshops as favourites
2 parents f52ebec + a4844a2 commit 432ea01

17 files changed

Lines changed: 200 additions & 145 deletions

app/src/main/java/net/squanchy/eventdetails/widget/EventDetailsCoordinatorLayout.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.squanchy.eventdetails.widget
33
import android.content.Context
44
import android.support.design.widget.CoordinatorLayout
55
import android.util.AttributeSet
6-
import android.view.View
6+
import androidx.view.isVisible
77
import kotlinx.android.synthetic.main.activity_event_details.view.*
88
import net.squanchy.R
99
import net.squanchy.schedule.domain.view.Event
@@ -20,20 +20,18 @@ class EventDetailsCoordinatorLayout @JvmOverloads constructor(
2020
eventDetailsHeaderLayout.updateWith(event, listener)
2121
eventDetailsLayout.updateWith(event)
2222

23-
if (canBeFavorited(event)) {
24-
favoriteFab.setImageResource(
25-
if (event.favorite)
26-
R.drawable.ic_favorite_filled
27-
else R.drawable.ic_favorite_empty
28-
)
23+
if (event.canBeFavorited) {
24+
val favoriteImage = if (event.favorite) R.drawable.ic_favorite_filled else R.drawable.ic_favorite_empty
25+
favoriteFab.setImageResource(favoriteImage)
2926
favoriteFab.setOnClickListener { listener.onFavoriteClick() }
30-
favoriteFab.visibility = View.VISIBLE
27+
favoriteFab.isVisible = true
3128
} else {
32-
favoriteFab.visibility = View.GONE
29+
favoriteFab.isVisible = false
3330
}
3431
}
3532

36-
private fun canBeFavorited(event: Event) = event.type === Type.TALK || event.type === Type.KEYNOTE
33+
private val Event.canBeFavorited
34+
get() = type == Type.TALK || type == Type.KEYNOTE || type == Type.WORKSHOP
3735

3836
internal interface OnEventDetailsClickListener : OnFavoriteClickListener, SpeakerView.OnSpeakerClickListener
3937

app/src/main/java/net/squanchy/eventdetails/widget/EventDetailsHeaderLayout.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ import android.content.Context
44
import android.support.design.widget.AppBarLayout
55
import android.util.AttributeSet
66
import android.view.View
7-
import kotlinx.android.synthetic.main.activity_event_details.view.titleTextView
8-
import kotlinx.android.synthetic.main.activity_event_details.view.speakerDetailsView
7+
import androidx.view.isVisible
8+
import kotlinx.android.synthetic.main.activity_event_details.view.*
99
import net.squanchy.schedule.domain.view.Event
1010
import net.squanchy.support.widget.SpeakerView
1111

1212
class EventDetailsHeaderLayout(context: Context, attrs: AttributeSet) : AppBarLayout(context, attrs) {
1313

1414
internal fun updateWith(event: Event, listener: SpeakerView.OnSpeakerClickListener) {
1515
titleTextView.text = event.title
16-
titleTextView.visibility = View.VISIBLE
16+
titleTextView.isVisible = true
1717

1818
speakerDetailsView.visibility = if (event.speakers.isEmpty()) View.GONE else View.VISIBLE
1919
speakerDetailsView.updateWith(event.speakers, listener)

app/src/main/java/net/squanchy/eventdetails/widget/EventDetailsLayout.kt

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import android.content.Context
44
import android.content.res.Resources
55
import android.support.annotation.AttrRes
66
import android.support.annotation.ColorInt
7+
import android.support.constraint.ConstraintLayout
78
import android.support.v4.content.ContextCompat
89
import android.text.SpannableStringBuilder
910
import android.text.Spanned
1011
import android.text.style.ForegroundColorSpan
1112
import android.util.AttributeSet
1213
import android.util.TypedValue
1314
import android.view.View
14-
import android.widget.LinearLayout
15+
import androidx.view.isVisible
1516
import arrow.core.Option
1617
import kotlinx.android.synthetic.main.merge_event_details_layout.view.*
1718
import net.squanchy.R
@@ -20,81 +21,76 @@ import net.squanchy.schedule.domain.view.Event
2021
import net.squanchy.schedule.domain.view.Place
2122
import net.squanchy.support.lang.getOrThrow
2223
import net.squanchy.support.text.parseHtml
24+
import org.joda.time.DateTimeZone
25+
import org.joda.time.LocalDateTime
2326
import org.joda.time.format.DateTimeFormat
2427

25-
// TODO flatten this layout as a ConstraintLayout
2628
class EventDetailsLayout @JvmOverloads constructor(
2729
context: Context,
2830
attrs: AttributeSet?,
2931
defStyle: Int = 0
30-
) : LinearLayout(context, attrs, defStyle) {
32+
) : ConstraintLayout(context, attrs, defStyle) {
3133

32-
init {
33-
super.setOrientation(LinearLayout.VERTICAL)
34-
}
35-
36-
override fun setOrientation(orientation: Int) {
37-
throw UnsupportedOperationException("Changing orientation is not supported for EventDetailsLayout")
38-
}
34+
private val dateTimeFormatter = DateTimeFormat.forPattern(WHEN_DATE_TIME_FORMAT)
3935

4036
override fun onFinishInflate() {
4137
super.onFinishInflate()
4238

43-
View.inflate(context, R.layout.merge_event_details_layout, this)
39+
inflate(context, R.layout.merge_event_details_layout, this)
4440
}
4541

4642
fun updateWith(event: Event) {
47-
updateWhen(event)
48-
updateWhere(event)
43+
updateWhen(event.startTime, event.timeZone)
44+
updateWhere(event.place)
4945
updateLevel(event.experienceLevel)
5046
updateDescription(event.description)
5147
}
5248

53-
private fun updateWhen(event: Event) {
54-
val formatter = DateTimeFormat.forPattern(WHEN_DATE_TIME_FORMAT).withZone(event.timeZone)
55-
whenTextView.text = formatter.print(event.startTime.toDateTime())
56-
whenContainer.visibility = View.VISIBLE
49+
private fun updateWhen(startTime: LocalDateTime, timeZone: DateTimeZone) {
50+
val formatter = dateTimeFormatter.withZone(timeZone)
51+
whenValue.text = formatter.print(startTime.toDateTime(timeZone))
52+
whenGroup.isVisible = true
5753
}
5854

59-
private fun updateWhere(event: Event) {
60-
if (event.place.isDefined()) {
61-
whereContainer.visibility = View.VISIBLE
62-
whereTextView.text = placeTextFrom(event.place.getOrThrow())
55+
private fun updateWhere(place: Option<Place>) {
56+
if (place.isDefined()) {
57+
whereGroup.isVisible = true
58+
whereValue.text = place.getOrThrow().toPlaceLabel()
6359
} else {
64-
whereContainer.visibility = View.GONE
60+
whereGroup.isVisible = false
6561
}
6662
}
6763

68-
private fun placeTextFrom(place: Place): CharSequence {
69-
val builder = SpannableStringBuilder(place.name)
70-
if (place.floor.isDefined()) {
71-
val floorLabel = place.floor.getOrThrow()
64+
private fun Place.toPlaceLabel(): CharSequence {
65+
val builder = SpannableStringBuilder(name)
66+
if (floor.isDefined()) {
67+
val floorLabel = floor.getOrThrow()
7268
builder.append(" ")
7369
.append(floorLabel)
7470
.setSpan(
75-
createColorSpan(whereTextView, android.R.attr.textColorSecondary),
76-
builder.length - floorLabel.length,
77-
builder.length,
78-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
71+
createColorSpan(whereValue, android.R.attr.textColorSecondary),
72+
builder.length - floorLabel.length,
73+
builder.length,
74+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
7975
)
8076
}
8177
return builder
8278
}
8379

8480
private fun updateLevel(level: Option<ExperienceLevel>) {
8581
if (level.isDefined()) {
86-
levelContainer.visibility = View.VISIBLE
82+
levelGroup.isVisible = true
8783

8884
val experienceLevel = level.getOrThrow()
89-
levelTextView.setText(experienceLevel.labelStringResId)
85+
levelValue.setText(experienceLevel.labelStringResId)
9086
tintCompoundDrawableEnd(experienceLevel)
9187
} else {
92-
levelContainer.visibility = View.GONE
88+
levelGroup.isVisible = false
9389
}
9490
}
9591

9692
private fun tintCompoundDrawableEnd(experienceLevel: ExperienceLevel) {
97-
val compoundDrawables = levelTextView.compoundDrawablesRelative
93+
val compoundDrawables = levelValue.compoundDrawablesRelative
9894
val endCompoundDrawable = compoundDrawables[2]
9995
endCompoundDrawable?.setTint(ContextCompat.getColor(context, experienceLevel.colorResId))
10096
}
@@ -113,12 +109,10 @@ class EventDetailsLayout @JvmOverloads constructor(
113109

114110
private fun updateDescription(description: Option<String>) {
115111
if (description.isDefined()) {
116-
descriptionHeader.visibility = View.VISIBLE
117-
descriptionTextView.visibility = View.VISIBLE
118-
descriptionTextView.text = parseHtml(description.getOrThrow())
112+
descriptionGroup.isVisible = true
113+
descriptionValue.text = description.getOrThrow().parseHtml()
119114
} else {
120-
descriptionHeader.visibility = View.GONE
121-
descriptionTextView.visibility = View.GONE
115+
descriptionGroup.isVisible = false
122116
}
123117
}
124118

app/src/main/java/net/squanchy/favorites/view/FavoritesSignedInEmptyLayout.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class FavoritesSignedInEmptyLayout @JvmOverloads constructor(
3434

3535
@RequiresApi(Build.VERSION_CODES.N)
3636
override fun showAchievement(message: String) {
37-
Snackbar.make(this, parseHtml(message), Snackbar.LENGTH_LONG).show()
37+
Snackbar.make(this, message.parseHtml(), Snackbar.LENGTH_LONG).show()
3838
}
3939

4040
private val favoriteButtonClickListener = View.OnClickListener {

app/src/main/java/net/squanchy/home/HomeActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import android.support.transition.TransitionManager
1212
import android.support.v7.app.AppCompatActivity
1313
import android.util.TypedValue
1414
import android.view.View
15+
import androidx.view.isInvisible
16+
import androidx.view.isVisible
1517
import kotlinx.android.synthetic.main.activity_home.*
1618
import net.squanchy.R
1719
import net.squanchy.analytics.Analytics
@@ -133,9 +135,9 @@ class HomeActivity : AppCompatActivity() {
133135

134136
private fun swapPageTo(section: BottomNavigationSection) {
135137
if (::currentSection.isInitialized) {
136-
pageViews[currentSection]!!.visibility = View.INVISIBLE
138+
pageViews[currentSection]!!.isInvisible = true
137139
}
138-
pageViews[section]!!.visibility = View.VISIBLE
140+
pageViews[section]!!.isVisible = true
139141
}
140142

141143
private fun getThemeFor(section: BottomNavigationSection): Resources.Theme {

app/src/main/java/net/squanchy/schedule/tracksfilter/ScheduleTracksFilter.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import android.view.ViewAnimationUtils
99
import android.view.animation.AnimationUtils
1010
import android.view.animation.Interpolator
1111
import androidx.animation.doOnEnd
12+
import androidx.view.isInvisible
13+
import androidx.view.isVisible
1214
import androidx.view.postOnAnimationDelayed
1315
import com.google.android.flexbox.FlexDirection
1416
import com.google.android.flexbox.FlexboxItemDecoration
@@ -96,7 +98,7 @@ class ScheduleTracksFilterActivity : AppCompatActivity() {
9698
duration = resources.getInteger(R.integer.track_filters_disappear_duration).toLong()
9799

98100
doOnEnd {
99-
filtersRoot.visibility = View.INVISIBLE
101+
filtersRoot.isInvisible = true
100102
finish()
101103
}
102104
}.start()
@@ -131,7 +133,7 @@ class ScheduleTracksFilterActivity : AppCompatActivity() {
131133

132134
private fun prepareAppearAnimation() {
133135
appearInterpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)
134-
filtersRoot.visibility = View.VISIBLE
136+
filtersRoot.isVisible = true
135137

136138
val titleDeltaY = resources.getDimension(R.dimen.track_filters_title_appear_delta_y)
137139
dialogTitle.apply {

app/src/main/java/net/squanchy/signin/SignInActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.support.design.widget.Snackbar
88
import android.support.v7.app.AppCompatActivity
99
import android.view.Gravity
1010
import android.view.View
11+
import androidx.view.isVisible
1112
import com.google.android.gms.auth.api.Auth
1213
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
1314
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
@@ -127,7 +128,7 @@ class SignInActivity : AppCompatActivity() {
127128
private fun showProgress() {
128129
signInContent.isEnabled = false
129130
signInContent.alpha = ALPHA_DISABLED
130-
progressView.visibility = View.VISIBLE
131+
progressView.isVisible = true
131132
}
132133

133134
private fun showSignInFailedError() {
@@ -138,7 +139,7 @@ class SignInActivity : AppCompatActivity() {
138139
private fun hideProgress() {
139140
signInContent.isEnabled = true
140141
signInContent.alpha = ALPHA_ENABLED
141-
progressView.visibility = View.GONE
142+
progressView.isVisible = false
142143
}
143144

144145
private fun signIn() {

app/src/main/java/net/squanchy/speaker/widget/SpeakerDetailsLayout.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ class SpeakerDetailsLayout(context: Context, attrs: AttributeSet) : ConstraintLa
1111

1212
fun updateWith(speaker: Speaker) {
1313
speakerDetailsHeader.updateWith(speaker)
14-
speakerBio.text = parseHtml(speaker.bio)
14+
speakerBio.text = speaker.bio.parseHtml()
1515
}
1616
}

app/src/main/java/net/squanchy/speaker/widget/SpeakerHeaderView.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.squanchy.speaker.widget
33
import android.content.Context
44
import android.support.constraint.ConstraintLayout
55
import android.util.AttributeSet
6-
import android.view.View
6+
import androidx.view.isVisible
77
import arrow.core.Option
88
import kotlinx.android.synthetic.main.activity_speaker_details.view.*
99
import net.squanchy.R
@@ -37,9 +37,9 @@ class SpeakerHeaderView @JvmOverloads constructor(
3737
if (companyName.isDefined()) {
3838
// TODO support navigating to company website
3939
speakerCompany.text = companyName.getOrThrow()
40-
speakerCompany.visibility = View.VISIBLE
40+
speakerCompany.isVisible = true
4141
} else {
42-
speakerCompany.visibility = View.GONE
42+
speakerCompany.isVisible = false
4343
}
4444
}
4545

@@ -49,7 +49,7 @@ class SpeakerHeaderView @JvmOverloads constructor(
4949
}
5050

5151
if (photoUrl.isDefined()) {
52-
speakerPhoto.visibility = View.VISIBLE
52+
speakerPhoto.isVisible = true
5353
imageLoader.load(photoUrl.getOrThrow())
5454
.error(R.drawable.ic_no_avatar)
5555
.into(speakerPhoto)

app/src/main/java/net/squanchy/support/text/Html.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import android.text.Html
66
import android.text.Spanned
77

88
@TargetApi(Build.VERSION_CODES.N) // The older fromHtml() is only called pre-24
9-
internal fun parseHtml(rawHtml: String): Spanned {
9+
internal fun String.parseHtml(): Spanned {
1010
// TODO use Dante (see https://github.com/squanchy-dev/squanchy-android/issues/322)
1111
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
12-
Html.fromHtml(rawHtml, Html.FROM_HTML_MODE_LEGACY)
12+
Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
1313
} else {
1414
@Suppress("DEPRECATION") // This is a "compat" method call, we only use this on pre-N
15-
Html.fromHtml(rawHtml)
15+
Html.fromHtml(this)
1616
}
1717
}

0 commit comments

Comments
 (0)