From edd092fab2a86ed40515b5ea7cb617e293dde3d3 Mon Sep 17 00:00:00 2001 From: Kenneth Ford Date: Mon, 18 Nov 2019 15:54:52 -0800 Subject: [PATCH 1/3] Added rail navigation fragment from gerrit, and created desktop layout file --- .../materialstudies/reply/ui/MainActivity.kt | 92 ++++++------ .../reply/ui/nav/RailNavigationFragment.kt | 135 ++++++++++++++++++ .../reply/util/FastOutUltraSlowIn.kt | 38 +++++ .../main/res/layout-w1024dp/activity_main.xml | 43 ++++++ .../res/layout/fragment_rail_navigation.xml | 115 +++++++++++++++ Reply/app/src/main/res/values/dimens.xml | 4 + 6 files changed, 384 insertions(+), 43 deletions(-) create mode 100644 Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt create mode 100644 Reply/app/src/main/java/com/materialstudies/reply/util/FastOutUltraSlowIn.kt create mode 100644 Reply/app/src/main/res/layout-w1024dp/activity_main.xml create mode 100644 Reply/app/src/main/res/layout/fragment_rail_navigation.xml diff --git a/Reply/app/src/main/java/com/materialstudies/reply/ui/MainActivity.kt b/Reply/app/src/main/java/com/materialstudies/reply/ui/MainActivity.kt index 09090b3..63eb824 100644 --- a/Reply/app/src/main/java/com/materialstudies/reply/ui/MainActivity.kt +++ b/Reply/app/src/main/java/com/materialstudies/reply/ui/MainActivity.kt @@ -45,8 +45,8 @@ class MainActivity : AppCompatActivity(), NavController.OnDestinationChangedListener { private val binding: ActivityMainBinding by contentView(R.layout.activity_main) - private val bottomNavDrawer: BottomNavDrawerFragment by lazy(NONE) { - supportFragmentManager.findFragmentById(R.id.bottom_nav_drawer) as BottomNavDrawerFragment + private val bottomNavDrawer: BottomNavDrawerFragment? by lazy(NONE) { + supportFragmentManager.findFragmentById(R.id.bottom_nav_drawer) as BottomNavDrawerFragment? } // Keep track of the current Email being viewed, if any, in order to pass the correct email id @@ -68,43 +68,49 @@ class MainActivity : AppCompatActivity(), } // Set a custom animation for showing and hiding the FAB - binding.fab.apply { + binding.fab?.let { fab -> + fab.apply { setShowMotionSpecResource(R.animator.fab_show) setHideMotionSpecResource(R.animator.fab_hide) setOnClickListener { findNavController(R.id.nav_host_fragment) - .navigate(ComposeFragmentDirections.actionGlobalComposeFragment(currentEmailId)) + .navigate(ComposeFragmentDirections.actionGlobalComposeFragment(currentEmailId)) + } } } - bottomNavDrawer.apply { - addOnSlideAction(HalfClockwiseRotateSlideAction(binding.bottomAppBarChevron)) - addOnSlideAction(AlphaSlideAction(binding.bottomAppBarTitle, true)) - addOnStateChangedAction(ShowHideFabStateAction(binding.fab)) - addOnStateChangedAction(ChangeSettingsMenuStateAction { showSettings -> - // Toggle between the current destination's BAB menu and the menu which should - // be displayed when the BottomNavigationDrawer is open. - binding.bottomAppBar.replaceMenu(if (showSettings) { - R.menu.bottom_app_bar_settings_menu - } else { - getBottomAppBarMenuForDestination() - }) - }) - - addOnSandwichSlideAction(HalfCounterClockwiseRotateSlideAction(binding.bottomAppBarChevron)) + bottomNavDrawer?.let { bottomNavBar -> + bottomNavBar.apply { + addOnSlideAction(HalfClockwiseRotateSlideAction(binding.bottomAppBarChevron!!)) + addOnSlideAction(AlphaSlideAction(binding.bottomAppBarTitle!!, true)) + addOnStateChangedAction(ShowHideFabStateAction(binding.fab!!)) + addOnStateChangedAction(ChangeSettingsMenuStateAction { showSettings -> + // Toggle between the current destination's BAB menu and the menu which should + // be displayed when the BottomNavigationDrawer is open. + binding.bottomAppBar?.replaceMenu(if (showSettings) { + R.menu.bottom_app_bar_settings_menu + } else { + getBottomAppBarMenuForDestination() + }) + }) + + addOnSandwichSlideAction(HalfCounterClockwiseRotateSlideAction(binding.bottomAppBarChevron!!)) + } } // Set up the BottomAppBar menu - binding.bottomAppBar.apply { - setNavigationOnClickListener { - bottomNavDrawer.toggle() + binding.bottomAppBar?.let { bottomAppBar -> + bottomAppBar.apply { + setNavigationOnClickListener { + bottomNavDrawer?.toggle() + } + setOnMenuItemClickListener(this@MainActivity) } - setOnMenuItemClickListener(this@MainActivity) } // Set up the BottomNavigationDrawer's open/close affordance - binding.bottomAppBarContentContainer.setOnClickListener { - bottomNavDrawer.toggle() + binding.bottomAppBarContentContainer?.setOnClickListener { + bottomNavDrawer?.toggle() } } @@ -154,42 +160,42 @@ class MainActivity : AppCompatActivity(), private fun setBottomAppBarForHome(@MenuRes menuRes: Int) { binding.run { - fab.setImageState(intArrayOf(-android.R.attr.state_activated), true) - bottomAppBar.visibility = View.VISIBLE - bottomAppBar.replaceMenu(menuRes) - fab.contentDescription = getString(R.string.fab_compose_email_content_description) - bottomAppBarTitle.visibility = View.VISIBLE - bottomAppBar.performShow() - fab.show() + fab?.setImageState(intArrayOf(-android.R.attr.state_activated), true) + bottomAppBar?.visibility = View.VISIBLE + bottomAppBar?.replaceMenu(menuRes) + fab?.contentDescription = getString(R.string.fab_compose_email_content_description) + bottomAppBarTitle?.visibility = View.VISIBLE + bottomAppBar?.performShow() + fab?.show() } } private fun setBottomAppBarForEmail(@MenuRes menuRes: Int) { binding.run { - fab.setImageState(intArrayOf(android.R.attr.state_activated), true) - bottomAppBar.visibility = View.VISIBLE - bottomAppBar.replaceMenu(menuRes) - fab.contentDescription = getString(R.string.fab_reply_email_content_description) - bottomAppBarTitle.visibility = View.INVISIBLE - bottomAppBar.performShow() - fab.show() + fab?.setImageState(intArrayOf(android.R.attr.state_activated), true) + bottomAppBar?.visibility = View.VISIBLE + bottomAppBar?.replaceMenu(menuRes) + fab?.contentDescription = getString(R.string.fab_reply_email_content_description) + bottomAppBarTitle?.visibility = View.INVISIBLE + bottomAppBar?.performShow() + fab?.show() } } private fun setBottomAppBarForCompose() { binding.run { - bottomAppBar.performHide() - fab.hide() + bottomAppBar?.performHide() + fab?.hide() // Hide the BottomAppBar to avoid it showing above the keyboard // when composing a new email. - bottomAppBar.visibility = View.GONE + bottomAppBar?.visibility = View.GONE } } override fun onMenuItemClick(item: MenuItem?): Boolean { when (item?.itemId) { R.id.menu_settings -> { - bottomNavDrawer.close() + bottomNavDrawer?.close() showDarkThemeMenu() } } diff --git a/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt b/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt new file mode 100644 index 0000000..0422d23 --- /dev/null +++ b/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt @@ -0,0 +1,135 @@ +package com.materialstudies.reply.ui.nav + +import android.animation.ValueAnimator +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.observe +import com.materialstudies.reply.R +import com.materialstudies.reply.databinding.FragmentRailNavigationBinding +import com.materialstudies.reply.util.FastOutUltraSlowIn +import com.materialstudies.reply.util.lerp +import kotlin.math.abs + +class RailNavigationFragment : Fragment(), NavigationAdapter.NavigationAdapterListener { + + /** + * Enumeration of states in which the account picker can be in. + */ + enum class RailState { + + /** + * The account picker is not visible. The navigation drawer is in its default state. + */ + CLOSED, + + /** + * the account picker is visible and open. + */ + OPEN, + + /** + * The account picker sandwiching animation is running. The account picker is neither open + * nor closed. + */ + SETTLING + } + + private lateinit var binding: FragmentRailNavigationBinding + + private var railState: RailState = RailState.OPEN + private var railAnim: ValueAnimator? = null + private val railInterp = FastOutUltraSlowIn() + private var railProgress: Float = 1F + set(value) { + if (field != value) { + onRailProgressChanged(value) + val newState = when (value) { + 1F -> RailState.OPEN + 0F -> RailState.CLOSED + else -> RailState.SETTLING + } + if (railState != newState) onRailStateChanged(newState) + railState = newState + field = value + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentRailNavigationBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.run { + val adapter = NavigationAdapter(this@RailNavigationFragment) + navRecyclerView.adapter = adapter + NavigationModel.navigationList.observe(this@RailNavigationFragment) { + adapter.submitList(it) + } + NavigationModel.setNavigationMenuItemChecked(0) + + composeFab.setOnClickListener { toggleRail() } + } + } + + override fun onNavMenuItemClicked(item: NavigationModelItem.NavMenuItem) { + // TODO + } + + override fun onNavEmailFolderClicked(folder: NavigationModelItem.NavEmailFolder) { + // TODO + } + + private fun toggleRail() { + val initialProgress = railProgress + val newProgress = when (railState) { + RailState.CLOSED -> 1F + RailState.OPEN -> 0F + RailState.SETTLING -> return + } + railAnim?.cancel() + railAnim = ValueAnimator.ofFloat(initialProgress, newProgress).apply { + addUpdateListener { railProgress = animatedValue as Float } + interpolator = railInterp + duration = (abs(newProgress - initialProgress) * 250F).toLong() + } + railAnim?.start() + } + + private fun onRailProgressChanged(progress: Float) { + // TODO + val railWidth = lerp( + resources.getDimension(R.dimen.min_rail_nav_width), + resources.getDimension(R.dimen.max_rail_nav_width), + 0F, + 1F, + progress + ) + + binding.run { + val params = railContainer.layoutParams + params.width = railWidth.toInt() + railContainer.layoutParams = params + + settingsIcon.alpha = progress + logoTitleTextView.alpha = progress + } + } + + private fun onRailStateChanged(state: RailState) { + // TODO: Look into animating the height of the fab manually instead of shrinking. +// when (state) { +// RailState.CLOSED -> { +// binding.composeFab.shrink() +// } +// } + } +} \ No newline at end of file diff --git a/Reply/app/src/main/java/com/materialstudies/reply/util/FastOutUltraSlowIn.kt b/Reply/app/src/main/java/com/materialstudies/reply/util/FastOutUltraSlowIn.kt new file mode 100644 index 0000000..d5dfcd6 --- /dev/null +++ b/Reply/app/src/main/java/com/materialstudies/reply/util/FastOutUltraSlowIn.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.materialstudies.reply.util + +import android.view.animation.Interpolator +import androidx.core.view.animation.PathInterpolatorCompat + +/** + * A custom Interpolator that dramatically slows as an animation end, avoiding sudden motion + * stops for large moving components (ie. shared element cards). + */ +class FastOutUltraSlowIn : Interpolator { + + private val pathInterpolator = PathInterpolatorCompat.create( + 0.185F, + 0.770F, + 0.135F, + 0.975F + ) + + override fun getInterpolation(fraction: Float): Float { + return pathInterpolator.getInterpolation(fraction) + } +} \ No newline at end of file diff --git a/Reply/app/src/main/res/layout-w1024dp/activity_main.xml b/Reply/app/src/main/res/layout-w1024dp/activity_main.xml new file mode 100644 index 0000000..58848f3 --- /dev/null +++ b/Reply/app/src/main/res/layout-w1024dp/activity_main.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/Reply/app/src/main/res/layout/fragment_rail_navigation.xml b/Reply/app/src/main/res/layout/fragment_rail_navigation.xml new file mode 100644 index 0000000..ad64f50 --- /dev/null +++ b/Reply/app/src/main/res/layout/fragment_rail_navigation.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Reply/app/src/main/res/values/dimens.xml b/Reply/app/src/main/res/values/dimens.xml index 457450b..28ebda5 100644 --- a/Reply/app/src/main/res/values/dimens.xml +++ b/Reply/app/src/main/res/values/dimens.xml @@ -32,4 +32,8 @@ 42dp 32dp + + + 300dp + 88dp From 7f63881d4ca8cc6dd4717318dc708268e131b880 Mon Sep 17 00:00:00 2001 From: Kenneth Ford Date: Mon, 18 Nov 2019 16:10:16 -0800 Subject: [PATCH 2/3] Created bool files to hold values to lock drawer open or not --- Reply/app/src/main/res/values-w1024dp/bool.xml | 4 ++++ Reply/app/src/main/res/values/bool.xml | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 Reply/app/src/main/res/values-w1024dp/bool.xml create mode 100644 Reply/app/src/main/res/values/bool.xml diff --git a/Reply/app/src/main/res/values-w1024dp/bool.xml b/Reply/app/src/main/res/values-w1024dp/bool.xml new file mode 100644 index 0000000..d9c8cd7 --- /dev/null +++ b/Reply/app/src/main/res/values-w1024dp/bool.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/Reply/app/src/main/res/values/bool.xml b/Reply/app/src/main/res/values/bool.xml new file mode 100644 index 0000000..49c6343 --- /dev/null +++ b/Reply/app/src/main/res/values/bool.xml @@ -0,0 +1,4 @@ + + + false + \ No newline at end of file From d9f6725a0d4dee297cff8e8e4d30fec5d3f6e7fe Mon Sep 17 00:00:00 2001 From: Kenneth Ford Date: Mon, 18 Nov 2019 16:22:30 -0800 Subject: [PATCH 3/3] Adds check if drawer is locked open or not to allow rail navigation --- .../materialstudies/reply/ui/nav/RailNavigationFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt b/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt index 0422d23..5e9b5ea 100644 --- a/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt +++ b/Reply/app/src/main/java/com/materialstudies/reply/ui/nav/RailNavigationFragment.kt @@ -76,7 +76,9 @@ class RailNavigationFragment : Fragment(), NavigationAdapter.NavigationAdapterLi } NavigationModel.setNavigationMenuItemChecked(0) - composeFab.setOnClickListener { toggleRail() } + if (!resources.getBoolean(R.bool.drawerLockedOpen)) { + logoImageView.setOnClickListener { toggleRail() } + } } }