Skip to content

Commit 524568e

Browse files
authored
Merge pull request #86 from odaridavid/improve-snackbar
Improve snackbars
2 parents 5505e7b + af292c6 commit 524568e

10 files changed

Lines changed: 81 additions & 36 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,10 @@ enhance user experience.
253253

254254
## Demo
255255

256-
<img src="art/appdemo.gif" width=200/>
256+
<img src="art/force_demo.gif" width=200/>
257257

258258

259-
<a href='https://play.google.com/store/apps/details?id=com.k0d4black.theforce&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' width='150'/></a>
259+
<a href='https://play.google.com/store/apps/details?id=com.k0d4black.theforce&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' width='170'/></a>
260260

261261
Google Play and the Google Play logo are trademarks of Google LLC.
262262

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.k0d4black.theforce.commons
22

3+
import android.app.Activity
34
import android.content.Context
45
import android.view.View
6+
import androidx.annotation.ColorRes
7+
import androidx.core.content.ContextCompat
58
import androidx.recyclerview.widget.DividerItemDecoration
69
import androidx.recyclerview.widget.LinearLayoutManager
710
import androidx.recyclerview.widget.RecyclerView
811
import com.google.android.material.snackbar.Snackbar
12+
import com.k0d4black.theforce.R
913

1014
fun View.show() {
1115
this.visibility = View.VISIBLE
@@ -15,13 +19,24 @@ fun View.hide() {
1519
this.visibility = View.INVISIBLE
1620
}
1721

18-
fun showSnackbar(view: View, message: String) {
19-
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show()
22+
fun Activity.showSnackbar(view: View, message: String, isError: Boolean = false) {
23+
val sb = Snackbar.make(view, message, Snackbar.LENGTH_LONG)
24+
if (isError)
25+
sb.setBackgroundTint(loadColor(R.color.colorError))
26+
.setTextColor(loadColor(R.color.colorOnError))
27+
.show()
28+
else
29+
sb.setBackgroundTint(loadColor(R.color.colorSecondary))
30+
.setTextColor(loadColor(R.color.colorOnSecondary))
31+
.show()
32+
2033
}
2134

2235
fun RecyclerView.initRecyclerViewWithLineDecoration(context: Context) {
2336
val linearLayoutManager = LinearLayoutManager(context)
24-
val itemDecoration = DividerItemDecoration(context, linearLayoutManager.orientation)
37+
val itemDecoration = DividerItemDecoration(context, linearLayoutManager.orientation).apply{
38+
setDrawable(context.getDrawable(R.drawable.view_divider)!!)
39+
}
2540
layoutManager = linearLayoutManager
2641
addItemDecoration(itemDecoration)
2742
}
@@ -30,3 +45,7 @@ fun Context.provideHorizontalLayoutManager(): LinearLayoutManager {
3045
return LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
3146
}
3247

48+
49+
fun Context.loadColor(@ColorRes colorRes: Int): Int {
50+
return ContextCompat.getColor(this, colorRes)
51+
}

app/src/main/kotlin/com/k0d4black/theforce/features/character_details/CharacterDetailActivity.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,19 @@ class CharacterDetailActivity : AppCompatActivity() {
5555

5656
renderPlanetDetails(it)
5757

58-
renderLoadingState(it)
58+
renderOnLoading(it)
5959

60-
renderError(it)
60+
renderOnError(it)
61+
62+
renderOnComplete(it)
6163
})
6264
}
6365

66+
private fun renderOnComplete(it: CharacterDetailsViewState) {
67+
if (it.isComplete)
68+
showSnackbar(binding.characterDetailsLayout, getString(R.string.info_loading_complete))
69+
}
70+
6471
private fun renderSpecies(it: CharacterDetailsViewState) {
6572
it.specie?.let { species ->
6673
binding.characterDetailsSpeciesRecyclerView.apply {
@@ -71,7 +78,7 @@ class CharacterDetailActivity : AppCompatActivity() {
7178
}
7279
}
7380

74-
private fun renderError(it: CharacterDetailsViewState) {
81+
private fun renderOnError(it: CharacterDetailsViewState) {
7582
it.error?.let { e ->
7683
displayErrorState(e.message)
7784
} ?: binding.loadingErrorTextView.hide()
@@ -94,7 +101,7 @@ class CharacterDetailActivity : AppCompatActivity() {
94101
}
95102
}
96103

97-
private fun renderLoadingState(it: CharacterDetailsViewState) {
104+
private fun renderOnLoading(it: CharacterDetailsViewState) {
98105
if (it.isLoading)
99106
binding.loadingCharacterProgressBar.show()
100107
else
@@ -104,7 +111,7 @@ class CharacterDetailActivity : AppCompatActivity() {
104111

105112
private fun displayErrorState(message: String) {
106113
binding.loadingErrorTextView.show()
107-
showSnackbar(character_details_layout, message)
114+
showSnackbar(character_details_layout, message, isError = true)
108115
}
109116

110117
private fun enableGroup(@IdRes groupId: Int) {

app/src/main/kotlin/com/k0d4black/theforce/features/character_details/CharacterDetailViewModel.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ internal class CharacterDetailViewModel(
3737
_detailViewState.value =
3838
CharacterDetailsViewState(
3939
isLoading = true,
40+
isComplete = false,
4041
error = null,
4142
planet = null,
4243
films = null,
@@ -52,7 +53,8 @@ internal class CharacterDetailViewModel(
5253
loadPlanet(characterUrl)
5354
loadFilms(characterUrl)
5455
loadSpecies(characterUrl)
55-
_detailViewState.value = _detailViewState.value?.copy(isLoading = false)
56+
_detailViewState.value =
57+
_detailViewState.value?.copy(isLoading = false, isComplete = true)
5658

5759
}
5860
}
@@ -87,7 +89,8 @@ internal class CharacterDetailViewModel(
8789
internal sealed class DetailViewState
8890
internal data class Error(val message: String) : DetailViewState()
8991
internal data class CharacterDetailsViewState(
90-
val isLoading: Boolean = false,
92+
val isLoading: Boolean,
93+
val isComplete: Boolean,
9194
val error: Error?,
9295
val planet: PlanetPresentation?,
9396
val films: List<FilmPresentation>?,

app/src/main/kotlin/com/k0d4black/theforce/features/character_search/SearchActivity.kt

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import android.view.Menu
66
import android.view.MenuItem
77
import androidx.appcompat.app.AppCompatActivity
88
import androidx.appcompat.widget.SearchView
9-
import androidx.core.view.isVisible
109
import androidx.databinding.DataBindingUtil
1110
import androidx.lifecycle.Observer
1211
import com.k0d4black.theforce.R
@@ -47,7 +46,7 @@ class SearchActivity : AppCompatActivity() {
4746
binding.searchResultsRecyclerView,
4847
getString(R.string.info_search_done)
4948
)
50-
displaySearchResults(it.searchResults)
49+
renderSearchResults(it.searchResults)
5150
}
5251
is Error -> displayErrorState(it.message)
5352
is Loading -> displayLoadingState()
@@ -65,27 +64,29 @@ class SearchActivity : AppCompatActivity() {
6564
}))
6665
}
6766

68-
private fun displaySearchResults(searchResultStarWars: List<CharacterPresentation>) {
67+
private fun renderSearchResults(searchResults: List<CharacterPresentation>) {
6968
val progressBar = binding.loadingSearchResultsProgressBar
70-
val searchTipTextView = binding.searchTipTextView
7169
progressBar.animate()
7270
.alpha(0f)
7371
.setListener(AnimatorListener(onEnd = { progressBar.hide() }))
7472

75-
if (searchResultStarWars.isNotEmpty()) {
76-
77-
if (searchTipTextView.isVisible) searchTipTextView.hide()
78-
79-
binding.searchResultsRecyclerView.apply {
80-
adapter = ScaleInAnimationAdapter(searchResultAdapter.apply {
81-
submitList(searchResultStarWars)
82-
})
83-
initRecyclerViewWithLineDecoration(this@SearchActivity)
84-
}
73+
if (searchResults.isEmpty()) {
74+
displayNoSearchResults()
75+
return
76+
}
8577

86-
binding.searchResultsRecyclerView.show()
78+
displaySearchResults(searchResults)
79+
}
8780

88-
} else displayNoSearchResults()
81+
private fun displaySearchResults(searchResults: List<CharacterPresentation>) {
82+
binding.searchTipTextView.hide()
83+
binding.searchResultsRecyclerView.apply {
84+
adapter = ScaleInAnimationAdapter(searchResultAdapter.apply {
85+
submitList(searchResults)
86+
})
87+
initRecyclerViewWithLineDecoration(this@SearchActivity)
88+
}
89+
binding.searchResultsRecyclerView.show()
8990
}
9091

9192
private fun displayNoSearchResults() {
@@ -104,7 +105,7 @@ class SearchActivity : AppCompatActivity() {
104105
.alpha(0f)
105106
.setListener(AnimatorListener(onEnd = { binding.loadingSearchResultsProgressBar.hide() }))
106107
showSearchTip()
107-
showSnackbar(binding.searchResultsRecyclerView, message)
108+
showSnackbar(binding.searchResultsRecyclerView, message, isError = true)
108109
}
109110

110111
private fun showSearchTip() {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?><!--
2+
~
3+
~ Copyright David Odari
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6+
~ in compliance with the License. You may obtain a copy of the License at
7+
~
8+
~ http://www.apache.org/licenses/LICENSE-2.0
9+
~
10+
~ Unless required by applicable law or agreed to in writing, software distributed under the License
11+
~ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12+
~ or implied. See the License for the specific language governing permissions and limitations under
13+
~ the License.
14+
-->
15+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
16+
android:shape="rectangle">
17+
<size android:height="1dp" />
18+
<solid android:color="@color/colorSecondary" />
19+
</shape>

app/src/main/res/layout/activity_character_detail.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
android:layout_height="wrap_content"
209209
android:layout_margin="@dimen/margin_default"
210210
android:drawableTop="@drawable/ic_error_24dp"
211-
android:text="@string/error_loading_character_details"
211+
android:text="@string/error_character_details"
212212
android:textAlignment="center"
213213
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
214214
android:visibility="gone" />

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<string name="content_description_more_info_arrow">View Character Details</string>
99
<string name="title_explore_activity">Explore</string>
1010
<string name="info_no_results">No Matching Search Results</string>
11+
<string name="info_search_done">Search Done</string>
1112

1213
<!-- Main Menu -->
1314
<string name="menu_title_search">Search</string>
@@ -20,6 +21,7 @@
2021
<string name="title_films">Films</string>
2122
<string name="title_species">Species</string>
2223
<string name="error_character_details">Couldn\'t load character details</string>
24+
<string name="info_loading_complete">Character Loaded Successfully</string>
2325

2426
<!-- Data Binding String Templates -->
2527
<string name="template_planet_name">Name : %s</string>
@@ -29,12 +31,6 @@
2931
<string name="template_height_inches">%s inches</string>
3032
<string name="template_species_language">Language : %s</string>
3133

32-
<!-- State Strings -->
33-
<string name="info_loading_status">Loading Character</string>
34-
<string name="error_loading_character_details">Couldn\'t load character details.</string>
35-
<string name="info_loading_complete">Character Loaded Successfully</string>
36-
<string name="info_search_done">Search Done</string>
37-
3834
<!-- Settings Activity -->
3935
<string name="info_app_version">Version %s</string>
4036
<string name="title_settings">Settings</string>

art/appdemo.gif

-6.05 MB
Binary file not shown.

art/force_demo.gif

1.38 MB
Loading

0 commit comments

Comments
 (0)