Initial adding bottom navigation

This commit is contained in:
Zakhar Timoshenko
2022-07-02 22:13:31 +03:00
parent 1d2584001f
commit 0e7960fced
9 changed files with 115 additions and 211 deletions

View File

@@ -22,6 +22,7 @@ import androidx.transition.TransitionManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.LayoutParams.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers
@@ -66,7 +67,6 @@ private const val TAG_SEARCH = "search"
class MainActivity :
BaseActivity<ActivityMainBinding>(),
NavigationView.OnNavigationItemSelectedListener,
AppBarOwner,
View.OnClickListener,
View.OnFocusChangeListener,
@@ -75,6 +75,8 @@ class MainActivity :
private val viewModel by viewModel<MainViewModel>()
private val searchSuggestionViewModel by viewModel<SearchSuggestionViewModel>()
private lateinit var nav: NavigationBarView
private lateinit var navHeaderBinding: NavigationHeaderBinding
private var drawerToggle: ActionBarDrawerToggle? = null
private var drawer: DrawerLayout? = null
@@ -87,6 +89,7 @@ class MainActivity :
super.onCreate(savedInstanceState)
setContentView(ActivityMainBinding.inflate(layoutInflater))
navHeaderBinding = NavigationHeaderBinding.inflate(layoutInflater)
nav = binding.bottomNav
drawer = binding.root as? DrawerLayout
drawerToggle = drawer?.let {
ActionBarDrawerToggle(
@@ -108,18 +111,33 @@ class MainActivity :
}
}
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
if (insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0) {
val elevation = binding.bottomNav.elevation
window.setNavigationBarTransparentCompat(this@MainActivity, elevation)
}
insets
}
ViewCompat.requestApplyInsets(binding.root)
with(binding.searchView) {
onFocusChangeListener = this@MainActivity
searchSuggestionListener = this@MainActivity
if (drawer == null) {
drawableStart = context.getThemeDrawable(materialR.attr.actionModeWebSearchDrawable)
}
}
with(binding.navigationView) {
ViewCompat.setOnApplyWindowInsetsListener(this, NavigationViewInsetsListener())
addHeaderView(navHeaderBinding.root)
setNavigationItemSelectedListener(this@MainActivity)
nav.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_local_storage -> {
viewModel.defaultSection = AppSection.HISTORY
setPrimaryFragment(HistoryListFragment.newInstance())
}
R.id.nav_favourites -> {
viewModel.defaultSection = AppSection.FAVOURITES
setPrimaryFragment(FavouritesContainerFragment.newInstance())
}
}
appBar.setExpanded(true)
true
}
binding.fab.setOnClickListener(this@MainActivity)
@@ -127,8 +145,6 @@ class MainActivity :
supportFragmentManager.findFragmentByTag(TAG_PRIMARY)?.let {
if (it is HistoryListFragment) binding.fab.show() else binding.fab.hide()
} ?: run {
openDefaultSection()
}
if (savedInstanceState == null) {
onFirstStart()
@@ -138,9 +154,6 @@ class MainActivity :
viewModel.onError.observe(this, this::onError)
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
viewModel.isResumeEnabled.observe(this, this::onResumeEnabledChanged)
viewModel.remoteSources.observe(this, this::updateSideMenu)
viewModel.isSuggestionsEnabled.observe(this, this::setSuggestionsEnabled)
viewModel.isTrackerEnabled.observe(this, this::setTrackerEnabled)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
@@ -163,9 +176,6 @@ class MainActivity :
val fragment = supportFragmentManager.findFragmentByTag(TAG_SEARCH)
binding.searchView.clearFocus()
when {
drawer?.isDrawerOpen(binding.navigationView) == true -> {
drawer?.closeDrawer(binding.navigationView)
}
fragment != null -> supportFragmentManager.commit {
remove(fragment)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
@@ -187,46 +197,6 @@ class MainActivity :
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
if (item.groupId == R.id.group_remote_sources) {
val source = MangaSource.values().getOrNull(item.itemId) ?: return false
setPrimaryFragment(RemoteListFragment.newInstance(source))
searchSuggestionViewModel.onSourceChanged(source)
} else {
searchSuggestionViewModel.onSourceChanged(null)
when (item.itemId) {
R.id.nav_history -> {
viewModel.defaultSection = AppSection.HISTORY
setPrimaryFragment(HistoryListFragment.newInstance())
}
R.id.nav_favourites -> {
viewModel.defaultSection = AppSection.FAVOURITES
setPrimaryFragment(FavouritesContainerFragment.newInstance())
}
R.id.nav_local_storage -> {
viewModel.defaultSection = AppSection.LOCAL
setPrimaryFragment(LocalListFragment.newInstance())
}
R.id.nav_suggestions -> {
viewModel.defaultSection = AppSection.SUGGESTIONS
setPrimaryFragment(SuggestionsFragment.newInstance())
}
R.id.nav_feed -> {
viewModel.defaultSection = AppSection.FEED
setPrimaryFragment(FeedFragment.newInstance())
}
R.id.nav_action_settings -> {
startActivity(SettingsActivity.newIntent(this))
return true
}
else -> return false
}
}
drawer?.closeDrawers()
appBar.setExpanded(true)
return true
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.fab.updateLayoutParams<MarginLayoutParams> {
bottomMargin = insets.bottom + topMargin
@@ -335,57 +305,6 @@ class MainActivity :
adjustFabVisibility(isResumeEnabled = isEnabled)
}
private fun updateSideMenu(remoteSources: List<MangaSource>) {
val submenu = binding.navigationView.menu.findItem(R.id.nav_remote_sources).subMenu
submenu.removeGroup(R.id.group_remote_sources)
remoteSources.forEachIndexed { index, source ->
submenu.add(R.id.group_remote_sources, source.ordinal, index, source.title)
.setIcon(R.drawable.ic_manga_source)
}
submenu.setGroupCheckable(R.id.group_remote_sources, true, true)
}
private fun setSuggestionsEnabled(isEnabled: Boolean) {
val item = binding.navigationView.menu.findItem(R.id.nav_suggestions) ?: return
if (!isEnabled && item.isChecked) {
binding.navigationView.setCheckedItem(R.id.nav_history)
}
item.isVisible = isEnabled
}
private fun setTrackerEnabled(isEnabled: Boolean) {
val item = binding.navigationView.menu.findItem(R.id.nav_feed) ?: return
if (!isEnabled && item.isChecked) {
binding.navigationView.setCheckedItem(R.id.nav_history)
}
item.isVisible = isEnabled
}
private fun openDefaultSection() {
when (viewModel.defaultSection) {
AppSection.LOCAL -> {
binding.navigationView.setCheckedItem(R.id.nav_local_storage)
setPrimaryFragment(LocalListFragment.newInstance())
}
AppSection.FAVOURITES -> {
binding.navigationView.setCheckedItem(R.id.nav_favourites)
setPrimaryFragment(FavouritesContainerFragment.newInstance())
}
AppSection.HISTORY -> {
binding.navigationView.setCheckedItem(R.id.nav_history)
setPrimaryFragment(HistoryListFragment.newInstance())
}
AppSection.FEED -> {
binding.navigationView.setCheckedItem(R.id.nav_feed)
setPrimaryFragment(FeedFragment.newInstance())
}
AppSection.SUGGESTIONS -> {
binding.navigationView.setCheckedItem(R.id.nav_suggestions)
setPrimaryFragment(SuggestionsFragment.newInstance())
}
}
}
private fun setPrimaryFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment, TAG_PRIMARY)

View File

@@ -0,0 +1,27 @@
package org.koitharu.kotatsu.utils
import android.content.Context
import android.content.res.Resources
object InternalResourceHelper {
fun getBoolean(context: Context, resName: String, defaultValue: Boolean): Boolean {
val id = getResourceId(resName, "bool")
return if (id != 0) {
context.createPackageContext("android", 0).resources.getBoolean(id)
} else {
defaultValue
}
}
/**
* Get resource id from system resources
* @param resName resource name to get
* @param type resource type of [resName] to get
* @return 0 if not available
*/
private fun getResourceId(resName: String, type: String): Int {
return Resources.getSystem().getIdentifier(resName, type, "android")
}
}

View File

@@ -2,19 +2,23 @@ package org.koitharu.kotatsu.utils.ext
import android.content.Context
import android.content.pm.ResolveInfo
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
import android.net.Uri
import android.os.Build
import android.view.Window
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ActivityOptionsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.work.CoroutineWorker
import com.google.android.material.elevation.ElevationOverlayProvider
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import org.koitharu.kotatsu.utils.InternalResourceHelper
import kotlin.coroutines.resume
val Context.connectivityManager: ConnectivityManager
@@ -66,4 +70,18 @@ fun Lifecycle.postDelayed(runnable: Runnable, delay: Long) {
delay(delay)
runnable.run()
}
}
fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float = 0F) {
navigationBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
!InternalResourceHelper.getBoolean(context, "config_navBarNeedsScrim", true)
) {
Color.TRANSPARENT
} else {
// Set navbar scrim 70% of navigationBarColor
ElevationOverlayProvider(context).compositeOverlayIfNeeded(
context.getResourceColor(android.R.attr.navigationBarColor, 0.7F),
elevation,
)
}
}

View File

@@ -1,11 +1,33 @@
package org.koitharu.kotatsu.utils.ext
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.Px
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import kotlin.math.roundToInt
@Px
fun Resources.resolveDp(dp: Int) = (dp * displayMetrics.density).roundToInt()
@Px
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
@ColorInt
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
val typedArray = obtainStyledAttributes(intArrayOf(resource))
val color = typedArray.getColor(0, 0)
typedArray.recycle()
if (alphaFactor < 1f) {
val alpha = (color.alpha * alphaFactor).roundToInt()
return Color.argb(alpha, color.red, color.green, color.blue)
}
return color
}

View File

@@ -2,6 +2,6 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?attr/colorSurfaceVariant" />
<solid android:color="?attr/colorSecondaryContainer" />
<corners android:radius="100dp" />
</shape>

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="260dp"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
app:drawerLayoutCornerSize="0dp"
app:elevation="0dp"
app:menu="@menu/nav_drawer" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:layout="@layout/fragment_list" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clipToPadding="false"
android:paddingLeft="16dp"
android:paddingRight="16dp"
app:elevation="0dp"
app:liftOnScroll="false">
<FrameLayout
android:id="@+id/toolbar_card"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_marginVertical="8dp"
android:background="@drawable/toolbar_background"
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
app:layout_scrollFlags="scroll|enterAlways">
<com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:focusable="true"
android:focusableInTouchMode="true">
<org.koitharu.kotatsu.search.ui.widget.SearchEditText
android:id="@+id/searchView"
style="@style/Widget.Kotatsu.SearchView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:background="@null"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:hint="@string/search_manga"
android:imeOptions="actionSearch"
android:importantForAutofill="no"
android:singleLine="true"
tools:ignore="TouchTargetSizeCheck" />
</com.google.android.material.appbar.MaterialToolbar>
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/_continue"
android:visibility="gone"
app:backgroundTint="?attr/colorContainer"
app:icon="@drawable/ic_read"
app:layout_anchor="@id/container"
app:layout_anchorGravity="bottom|end"
app:layout_behavior="org.koitharu.kotatsu.base.ui.util.ShrinkOnScrollBehavior"
app:layout_dodgeInsetEdges="bottom"
tools:visibility="visible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@@ -24,15 +24,13 @@
android:paddingRight="16dp"
android:stateListAnimator="@null">
<com.google.android.material.card.MaterialCardView
<FrameLayout
android:id="@+id/toolbar_card"
style="@style/Widget.Material3.CardView.Elevated"
android:background="@drawable/toolbar_background"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginVertical="8dp"
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
app:cardCornerRadius="50dp"
app:cardElevation="2dp"
app:layout_scrollFlags="scroll|enterAlways">
<com.google.android.material.appbar.MaterialToolbar
@@ -42,7 +40,9 @@
android:background="@null"
android:focusable="true"
android:focusableInTouchMode="true"
app:contentInsetStartWithNavigation="0dp">
app:contentInsetStartWithNavigation="0dp"
android:layout_marginEnd="6dp"
app:navigationIcon="?attr/actionModeWebSearchDrawable">
<org.koitharu.kotatsu.search.ui.widget.SearchEditText
android:id="@+id/searchView"
@@ -60,7 +60,7 @@
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>