Skip to content
This repository was archived by the owner on Nov 21, 2024. It is now read-only.

Commit 35e9bd0

Browse files
committed
HomeFragment polish, improvements
- ShapeAppearance overlays for views attached to bottom of screen and cards in EmailAdapter - MenuBottomSheetDialogFragmnet refactored to use NavigationView - Added dark theme selection menu options - Added databinding - Refactored appcompat views to framework views - Refactored to use ?attr for referencing theme/style attributes - Added shape dimens in shape.xml - Added content descriptions Change-Id: Id2f8f40d51a6d29a45a112a7bc4f9b9bb4ca1d5a
1 parent f19f331 commit 35e9bd0

File tree

87 files changed

+1068
-608
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1068
-608
lines changed

app/build.gradle

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
apply plugin: 'com.android.application'
2-
32
apply plugin: 'kotlin-android'
4-
5-
apply plugin: 'kotlin-android-extensions'
3+
apply plugin: 'kotlin-kapt'
64

75
android {
8-
compileSdkVersion 28
6+
compileSdkVersion 29
97
defaultConfig {
108
applicationId "com.materialstudies.reply"
11-
minSdkVersion 21
12-
targetSdkVersion 28
9+
minSdkVersion 23
10+
targetSdkVersion 29
1311
versionCode 1
1412
versionName "1.0"
1513
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1614
}
15+
dataBinding {
16+
enabled true
17+
}
1718
buildTypes {
1819
release {
1920
minifyEnabled false
@@ -29,20 +30,23 @@ dependencies {
2930
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
3031

3132
// AndroidX
32-
implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
33+
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
3334
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
3435
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
35-
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05'
36+
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha06'
3637
implementation 'androidx.core:core-ktx:1.0.2'
37-
implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0-alpha04'
38-
implementation 'androidx.navigation:navigation-ui-ktx:2.1.0-alpha04'
38+
implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0-alpha05'
39+
implementation 'androidx.navigation:navigation-ui-ktx:2.1.0-alpha05'
40+
41+
// Glide
42+
implementation "com.github.bumptech.glide:glide:4.9.0"
3943

4044
// Material Components
41-
implementation 'com.google.android.material:material:1.1.0-alpha06'
45+
implementation 'com.google.android.material:material:1.1.0-alpha07'
4246

4347
// Testing
4448
testImplementation 'junit:junit:4.12'
45-
androidTestImplementation 'androidx.test:runner:1.2.0-beta01'
46-
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01'
49+
androidTestImplementation 'androidx.test:runner:1.2.0'
50+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
4751

4852
}
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
package com.materialstudies.reply.data
22

3+
import androidx.recyclerview.widget.DiffUtil
4+
35
/**
46
* A simple data class to represent an Email.
57
*/
68
data class Email(
7-
val id: Int,
8-
val sender: String,
9-
val subject: String,
10-
val body: String,
11-
val senderImg: Int,
12-
var isStarred: Boolean = false
13-
)
9+
val id: Int,
10+
val sender: String,
11+
val subject: String,
12+
val body: String,
13+
val senderImg: Int,
14+
val attachments: List<EmailAttachment> = emptyList(),
15+
var isImportant: Boolean = false,
16+
var isStarred: Boolean = false
17+
) {
18+
val hasBody: Boolean = body.isNotBlank()
19+
val hasAttachments: Boolean = attachments.isNotEmpty()
20+
}
21+
22+
object EmailDiffCallback : DiffUtil.ItemCallback<Email>() {
23+
override fun areItemsTheSame(oldItem: Email, newItem: Email): Boolean = oldItem.id == newItem.id
24+
25+
override fun areContentsTheSame(oldItem: Email, newItem: Email): Boolean {
26+
return oldItem.sender == newItem.sender &&
27+
oldItem.subject == newItem.subject &&
28+
oldItem.body == newItem.body &&
29+
oldItem.senderImg == newItem.senderImg &&
30+
oldItem.isStarred == newItem.isStarred &&
31+
oldItem.isImportant == newItem.isImportant &&
32+
oldItem.attachments.containsAll(newItem.attachments)
33+
34+
}
35+
}
1436

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.materialstudies.reply.data
2+
3+
import androidx.annotation.DrawableRes
4+
5+
data class EmailAttachment(
6+
@DrawableRes val resId: Int,
7+
val contentDesc: String
8+
)

app/src/main/java/com/materialstudies/reply/data/EmailStore.kt

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,67 +5,77 @@ import androidx.lifecycle.MutableLiveData
55
import com.materialstudies.reply.R
66

77
/**
8-
* A static data store of [Email]'s.
8+
* A static data store of [Email]s.
99
*/
1010
class EmailStore {
1111

1212
private val allEmails = mutableListOf(
13-
Email(
14-
0,
15-
"Google Express - 15m ago",
16-
"Package shipped!",
17-
"Cucumber Mask Facial has shipped",
18-
R.drawable.avatar_express
13+
Email(
14+
0,
15+
"Google Express - 15m ago",
16+
"Package shipped!",
17+
"Cucumber Mask Facial has shipped",
18+
R.drawable.avatar_express,
19+
isStarred = true
20+
),
21+
Email(
22+
1,
23+
"Ali Connors - 25m ago",
24+
"Brunch this weekend?",
25+
"I'll be in your neighborhood doing errands...",
26+
R.drawable.avatar_1
27+
),
28+
Email(
29+
2,
30+
"Sandra Adams - 6 hrs ago",
31+
"Bonjour from Paris",
32+
"Here are some great shots from my trip...",
33+
R.drawable.avatar_2,
34+
listOf(
35+
EmailAttachment(R.drawable.paris_1, "Bridge in Paris"),
36+
EmailAttachment(R.drawable.paris_2, "Bridge in Paris at night"),
37+
EmailAttachment(R.drawable.paris_3, "City street in Paris"),
38+
EmailAttachment(R.drawable.paris_4, "Street with bike in Paris")
1939
),
20-
Email(
21-
1,
22-
"Ali Connors - 25m ago",
23-
"Brunch this weekend?",
24-
"I'll be in your neighborhood doing errands...",
25-
R.drawable.avatar1
26-
),
27-
Email(
28-
2,
29-
"Sandra Adams - 6 hrs ago",
30-
"Bonjour from Paris",
31-
"Here are some great shots from my trip...",
32-
R.drawable.avatar2
33-
),
34-
Email(
35-
3,
36-
"Trevor Hansen - 12 hrs ago",
37-
"High school reunion?",
38-
"",
39-
R.drawable.avatar3
40-
),
41-
Email(
42-
4,
43-
"Britta Holt - 18 hrs ago",
44-
"Brazil trip",
45-
"Thought we might be able to go over some details about our upcoming vacation...",
46-
R.drawable.avatar4
47-
),
48-
Email(
49-
5,
50-
"Frank Hawkins - 20 hrs ago",
51-
"Update to Your Itinerary",
52-
"",
53-
R.drawable.avatar5
54-
),
55-
Email(
56-
6,
57-
"Britta Holt - 21 hrs ago",
58-
"Recipe to try",
59-
"Raspberry Pie: We should make this pie recipe tonight! The filling is very quick to put together.",
60-
R.drawable.avatar6
61-
),
62-
Email(
63-
7,
64-
"Google Express - 22 hrs ago",
65-
"Delivered",
66-
"Your shoes should be waiting for you at home!",
67-
R.drawable.avatar7
68-
)
40+
true
41+
),
42+
Email(
43+
3,
44+
"Trevor Hansen - 12 hrs ago",
45+
"High school reunion?",
46+
"",
47+
R.drawable.avatar_3
48+
),
49+
Email(
50+
4,
51+
"Britta Holt - 18 hrs ago",
52+
"Brazil trip",
53+
"Thought we might be able to go over some details about our upcoming vacation...",
54+
R.drawable.avatar_4,
55+
isStarred = true
56+
),
57+
Email(
58+
5,
59+
"Frank Hawkins - 20 hrs ago",
60+
"Update to Your Itinerary",
61+
"",
62+
R.drawable.avatar_5
63+
),
64+
Email(
65+
6,
66+
"Britta Holt - 21 hrs ago",
67+
"Recipe to try",
68+
"Raspberry Pie: We should make this pie recipe tonight! The filling is " +
69+
"very quick to put together.",
70+
R.drawable.avatar_6
71+
),
72+
Email(
73+
7,
74+
"Google Express - 22 hrs ago",
75+
"Delivered",
76+
"Your shoes should be waiting for you at home!",
77+
R.drawable.avatar_7
78+
)
6979
)
7080

7181
private val _emails: MutableLiveData<List<Email>> = MutableLiveData()
Lines changed: 35 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,79 @@
11
package com.materialstudies.reply.ui
22

3-
import android.content.Context
43
import androidx.appcompat.app.AppCompatActivity
54
import android.os.Bundle
65
import android.view.MenuItem
7-
import android.view.View
8-
import android.widget.LinearLayout
9-
import android.widget.Toast
106
import androidx.appcompat.app.AppCompatDelegate
11-
import androidx.appcompat.widget.AppCompatImageView
12-
import androidx.appcompat.widget.AppCompatTextView
137
import androidx.appcompat.widget.Toolbar
14-
import androidx.coordinatorlayout.widget.CoordinatorLayout
15-
import androidx.core.view.ViewCompat
16-
import androidx.core.view.WindowInsetsCompat
17-
import com.google.android.material.bottomappbar.BottomAppBar
18-
import com.google.android.material.floatingactionbutton.FloatingActionButton
198
import com.materialstudies.reply.R
9+
import com.materialstudies.reply.databinding.ActivityMainBinding
2010
import com.materialstudies.reply.ui.nav.AlphaSlideAction
21-
import com.materialstudies.reply.ui.nav.BottomNavigationDrawer
2211
import com.materialstudies.reply.ui.nav.ChangeSettingsMenuStateAction
2312
import com.materialstudies.reply.ui.nav.QuarterRotateSlideAction
2413
import com.materialstudies.reply.ui.nav.ShowHideFabStateAction
14+
import com.materialstudies.reply.util.contentView
2515

2616
class MainActivity : AppCompatActivity(), Toolbar.OnMenuItemClickListener {
2717

28-
private lateinit var coordinatorLayout: CoordinatorLayout
29-
private lateinit var bottomAppBar: BottomAppBar
30-
private lateinit var bottomAppBarContentContainer: LinearLayout
31-
private lateinit var bottomAppBarChevron: AppCompatImageView
32-
private lateinit var bottomAppBarTitleTextView: AppCompatTextView
33-
private lateinit var bottomNavigationDrawer: BottomNavigationDrawer
34-
private lateinit var fab: FloatingActionButton
18+
private val binding: ActivityMainBinding by contentView(R.layout.activity_main)
3519

3620
override fun onCreate(savedInstanceState: Bundle?) {
3721
super.onCreate(savedInstanceState)
38-
setContentView(R.layout.activity_main)
39-
40-
// Draw behind all system bars
41-
val decor = window.decorView
42-
val flags = decor.systemUiVisibility or
43-
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
44-
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
45-
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
46-
decor.systemUiVisibility = flags
47-
48-
coordinatorLayout = findViewById(R.id.coordinator_layout)
49-
bottomAppBar = findViewById(R.id.bottom_app_bar)
50-
bottomAppBarContentContainer = findViewById(R.id.bottom_app_bar_content_container)
51-
bottomAppBarChevron = findViewById(R.id.bottom_app_bar_chevron)
52-
bottomAppBarTitleTextView = findViewById(R.id.bottom_app_bar_title)
53-
bottomNavigationDrawer = findViewById(R.id.bottom_navigation_drawer)
54-
fab = findViewById(R.id.fab)
55-
56-
ViewCompat.setOnApplyWindowInsetsListener(coordinatorLayout) { _, insets ->
57-
handleApplyWindowInsets(insets)
58-
}
59-
6022
setUpBottomNavigationAndFab()
6123
}
6224

63-
fun handleMenuBottomSheetItemClicked(entry: MenuBottomSheetDialogFragment.MenuEntry) {
64-
Toast.makeText(this, getString(entry.title), Toast.LENGTH_SHORT).show()
65-
}
66-
67-
private fun handleApplyWindowInsets(insets: WindowInsetsCompat): WindowInsetsCompat {
68-
bottomAppBar.layoutParams.height = getBottomAppBarHeight(this, insets)
69-
return insets
70-
}
71-
7225
private fun setUpBottomNavigationAndFab() {
73-
fab.setShowMotionSpecResource(R.animator.fab_show)
74-
fab.setHideMotionSpecResource(R.animator.fab_hide)
26+
binding.fab.setShowMotionSpecResource(R.animator.fab_show)
27+
binding.fab.setHideMotionSpecResource(R.animator.fab_hide)
7528

76-
bottomNavigationDrawer.apply {
77-
addOnSlideAction(QuarterRotateSlideAction(bottomAppBarChevron))
78-
addOnSlideAction(AlphaSlideAction(bottomAppBarTitleTextView, true))
79-
addOnStateChangedAction(ShowHideFabStateAction(fab))
29+
binding.bottomNavigationDrawer.apply {
30+
addOnSlideAction(QuarterRotateSlideAction(binding.bottomAppBarChevron))
31+
addOnSlideAction(AlphaSlideAction(binding.bottomAppBarTitle, true))
32+
addOnStateChangedAction(ShowHideFabStateAction(binding.fab))
8033
addOnStateChangedAction(ChangeSettingsMenuStateAction { showSettings ->
81-
bottomAppBar.replaceMenu(if (showSettings) {
34+
binding.bottomAppBar.replaceMenu(if (showSettings) {
8235
R.menu.bottom_app_bar_settings_menu
8336
} else {
8437
R.menu.bottom_app_bar_home_menu
8538
})
8639
})
8740
}
8841

89-
bottomAppBar.setNavigationOnClickListener { bottomNavigationDrawer.toggle() }
90-
bottomAppBar.setOnMenuItemClickListener(this)
91-
bottomAppBarContentContainer.setOnClickListener { bottomNavigationDrawer.toggle() }
42+
binding.bottomAppBar.setNavigationOnClickListener {
43+
binding.bottomNavigationDrawer.toggle()
44+
}
45+
binding.bottomAppBar.setOnMenuItemClickListener(this)
46+
binding.bottomAppBarContentContainer.setOnClickListener {
47+
binding.bottomNavigationDrawer.toggle()
48+
}
9249
}
9350

9451
override fun onMenuItemClick(item: MenuItem?): Boolean {
9552
when (item?.itemId) {
96-
R.id.menu_theme -> toggleTheme()
53+
R.id.menu_theme -> {
54+
binding.bottomNavigationDrawer.close()
55+
showDarkThemeMenu()
56+
}
9757
}
9858
return true
9959
}
10060

101-
private fun toggleTheme() {
102-
delegate.localNightMode = if (delegate.localNightMode == AppCompatDelegate.MODE_NIGHT_YES) {
103-
AppCompatDelegate.MODE_NIGHT_NO
104-
} else {
105-
AppCompatDelegate.MODE_NIGHT_YES
106-
}
61+
private fun showDarkThemeMenu() {
62+
MenuBottomSheetDialogFragment(R.menu.dark_theme_bottom_sheet_menu) {
63+
onDarkThemeMenuItemSelected(it.itemId)
64+
}.show(supportFragmentManager, null)
10765
}
10866

109-
companion object {
110-
fun getBottomAppBarHeight(context: Context, insets: WindowInsetsCompat): Int {
111-
return context.resources.getDimensionPixelSize(
112-
R.dimen.bottom_app_bar_height
113-
) + insets.systemWindowInsetBottom
67+
private fun onDarkThemeMenuItemSelected(itemId: Int): Boolean {
68+
val nightMode = when (itemId) {
69+
R.id.menu_light -> AppCompatDelegate.MODE_NIGHT_NO
70+
R.id.menu_dark -> AppCompatDelegate.MODE_NIGHT_YES
71+
R.id.menu_battery_saver -> AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
72+
R.id.menu_system_default -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
73+
else -> return false
11474
}
75+
76+
delegate.localNightMode = nightMode
77+
return true
11578
}
11679
}

0 commit comments

Comments
 (0)