Simple screens and logic with Navigation Component
This commit is contained in:
Binary file not shown.
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 2,
|
|
||||||
"artifactType": {
|
|
||||||
"type": "APK",
|
|
||||||
"kind": "Directory"
|
|
||||||
},
|
|
||||||
"applicationId": "com.meloda.fast",
|
|
||||||
"variantName": "processReleaseResources",
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"type": "SINGLE",
|
|
||||||
"filters": [],
|
|
||||||
"versionCode": 1,
|
|
||||||
"versionName": "1.0",
|
|
||||||
"outputFile": "app-release.apk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
package com.meloda.fast.base
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.SparseArray
|
||||||
|
import androidx.core.util.forEach
|
||||||
|
import androidx.core.util.set
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
import com.meloda.fast.R
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
fun BottomNavigationView.setupWithNavController(
|
||||||
|
navGraphIds: List<Int>,
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
dataManager: DataManager,
|
||||||
|
containerId: Int,
|
||||||
|
intent: Intent
|
||||||
|
): LiveData<NavController> {
|
||||||
|
|
||||||
|
// Map of tags
|
||||||
|
val graphIdToTagMap = SparseArray<String>()
|
||||||
|
// Result. Mutable live data with the selected controlled
|
||||||
|
val selectedNavController = MutableLiveData<NavController>()
|
||||||
|
|
||||||
|
var firstFragmentGraphId = 0
|
||||||
|
|
||||||
|
// First create a NavHostFragment for each NavGraph ID
|
||||||
|
navGraphIds.forEachIndexed { index, navGraphId ->
|
||||||
|
val fragmentTag = getFragmentTag(index)
|
||||||
|
|
||||||
|
// Find or create the Navigation host fragment
|
||||||
|
val navHostFragment = obtainNavHostFragment(
|
||||||
|
fragmentManager,
|
||||||
|
fragmentTag,
|
||||||
|
navGraphId,
|
||||||
|
containerId
|
||||||
|
)
|
||||||
|
|
||||||
|
// Obtain its id
|
||||||
|
val graphId = navHostFragment.navController.graph.id
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
firstFragmentGraphId = graphId
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to the map
|
||||||
|
graphIdToTagMap[graphId] = fragmentTag
|
||||||
|
|
||||||
|
// Attach or detach nav host fragment depending on whether it's the selected item.
|
||||||
|
if (this.selectedItemId == graphId) {
|
||||||
|
// Update livedata with the selected graph
|
||||||
|
selectedNavController.value = navHostFragment.navController
|
||||||
|
attachNavHostFragment(fragmentManager, navHostFragment, index == 0)
|
||||||
|
} else {
|
||||||
|
detachNavHostFragment(fragmentManager, navHostFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now connect selecting an item with swapping Fragments
|
||||||
|
var selectedItemTag = graphIdToTagMap[this.selectedItemId]
|
||||||
|
val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
|
||||||
|
var isOnFirstFragment = selectedItemTag == firstFragmentTag
|
||||||
|
|
||||||
|
// When a navigation item is selected
|
||||||
|
setOnNavigationItemSelectedListener { item ->
|
||||||
|
// Don't do anything if the state is state has already been saved.
|
||||||
|
if (fragmentManager.isStateSaved) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
val navController = (fragmentManager.findFragmentByTag(selectedItemTag) as NavHostFragment).navController
|
||||||
|
navController.popBackStack(navController.graph.startDestination, false)
|
||||||
|
if (selectedItemTag != graphIdToTagMap[item.itemId]) {
|
||||||
|
val listCheck = listOf(R.id.contacts, R.id.chats)
|
||||||
|
val newlySelectedItemTag = if (listCheck.contains(item.itemId) && dataManager.token.isBlank()) graphIdToTagMap[R.id.signIn] else graphIdToTagMap[item.itemId]
|
||||||
|
|
||||||
|
// Pop everything above the first fragment (the "fixed start destination")
|
||||||
|
fragmentManager.popBackStack(
|
||||||
|
firstFragmentTag,
|
||||||
|
FragmentManager.POP_BACK_STACK_INCLUSIVE
|
||||||
|
)
|
||||||
|
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment
|
||||||
|
|
||||||
|
// Exclude the first fragment tag because it's always in the back stack.
|
||||||
|
if (firstFragmentTag != newlySelectedItemTag) {
|
||||||
|
// Commit a transaction that cleans the back stack and adds the first fragment
|
||||||
|
// to it, creating the fixed started destination.
|
||||||
|
fragmentManager.beginTransaction()
|
||||||
|
.setCustomAnimations(
|
||||||
|
R.anim.nav_default_enter_anim,
|
||||||
|
R.anim.nav_default_exit_anim,
|
||||||
|
R.anim.nav_default_pop_enter_anim,
|
||||||
|
R.anim.nav_default_pop_exit_anim
|
||||||
|
)
|
||||||
|
.attach(selectedFragment)
|
||||||
|
.setPrimaryNavigationFragment(selectedFragment)
|
||||||
|
.apply {
|
||||||
|
// Detach all other Fragments
|
||||||
|
graphIdToTagMap.forEach { _, fragmentTagIter ->
|
||||||
|
if (fragmentTagIter != newlySelectedItemTag) {
|
||||||
|
detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.addToBackStack(firstFragmentTag)
|
||||||
|
.setReorderingAllowed(true)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
selectedItemTag = newlySelectedItemTag
|
||||||
|
isOnFirstFragment = selectedItemTag == firstFragmentTag
|
||||||
|
selectedNavController.value = selectedFragment.navController
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOnNavigationItemReselectedListener { item ->
|
||||||
|
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
|
||||||
|
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment
|
||||||
|
val navController = selectedFragment.navController
|
||||||
|
// Pop the back stack to the start destination of the current navController graph
|
||||||
|
if (selectedItemTag != graphIdToTagMap[item.itemId] && dataManager.token.isNotBlank()) {
|
||||||
|
fragmentManager.beginTransaction()
|
||||||
|
.setCustomAnimations(
|
||||||
|
R.anim.nav_default_enter_anim,
|
||||||
|
R.anim.nav_default_exit_anim,
|
||||||
|
R.anim.nav_default_pop_enter_anim,
|
||||||
|
R.anim.nav_default_pop_exit_anim
|
||||||
|
)
|
||||||
|
.attach(selectedFragment)
|
||||||
|
.setPrimaryNavigationFragment(selectedFragment)
|
||||||
|
.apply {
|
||||||
|
// Detach all other Fragments
|
||||||
|
graphIdToTagMap.forEach { _, fragmentTagIter ->
|
||||||
|
if (fragmentTagIter != newlySelectedItemTag) {
|
||||||
|
detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.addToBackStack(firstFragmentTag)
|
||||||
|
.setReorderingAllowed(true)
|
||||||
|
.commit()
|
||||||
|
selectedItemTag = newlySelectedItemTag
|
||||||
|
isOnFirstFragment = selectedItemTag == firstFragmentTag
|
||||||
|
selectedNavController.value = selectedFragment.navController
|
||||||
|
} else navController.popBackStack(navController.graph.startDestination, false)
|
||||||
|
}
|
||||||
|
// Optional: on item reselected, pop back stack to the destination of the graph
|
||||||
|
setupDeepLinks(navGraphIds, fragmentManager, containerId, intent)
|
||||||
|
|
||||||
|
// Finally, ensure that we update our BottomNavigationView when the back stack changes
|
||||||
|
fragmentManager.addOnBackStackChangedListener {
|
||||||
|
if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) {
|
||||||
|
this.selectedItemId = firstFragmentGraphId
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the graph if the currentDestination is not valid (happens when the back
|
||||||
|
// stack is popped after using the back button).
|
||||||
|
selectedNavController.value?.let { controller ->
|
||||||
|
if (controller.currentDestination == null) {
|
||||||
|
controller.navigate(controller.graph.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedNavController
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BottomNavigationView.setupDeepLinks(
|
||||||
|
navGraphIds: List<Int>,
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
containerId: Int,
|
||||||
|
intent: Intent
|
||||||
|
) {
|
||||||
|
navGraphIds.forEachIndexed { index, navGraphId ->
|
||||||
|
val fragmentTag = getFragmentTag(index)
|
||||||
|
|
||||||
|
// Find or create the Navigation host fragment
|
||||||
|
val navHostFragment = obtainNavHostFragment(
|
||||||
|
fragmentManager,
|
||||||
|
fragmentTag,
|
||||||
|
navGraphId,
|
||||||
|
containerId
|
||||||
|
)
|
||||||
|
// Handle Intent
|
||||||
|
if (navHostFragment.navController.handleDeepLink(intent) &&
|
||||||
|
selectedItemId != navHostFragment.navController.graph.id
|
||||||
|
) {
|
||||||
|
this.selectedItemId = navHostFragment.navController.graph.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun detachNavHostFragment(
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
navHostFragment: NavHostFragment
|
||||||
|
) {
|
||||||
|
fragmentManager.beginTransaction()
|
||||||
|
.detach(navHostFragment)
|
||||||
|
.commitNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachNavHostFragment(
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
navHostFragment: NavHostFragment,
|
||||||
|
isPrimaryNavFragment: Boolean
|
||||||
|
) {
|
||||||
|
fragmentManager.beginTransaction()
|
||||||
|
.attach(navHostFragment)
|
||||||
|
.apply {
|
||||||
|
if (isPrimaryNavFragment) {
|
||||||
|
setPrimaryNavigationFragment(navHostFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.commitNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun obtainNavHostFragment(
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
fragmentTag: String,
|
||||||
|
navGraphId: Int,
|
||||||
|
containerId: Int,
|
||||||
|
): NavHostFragment {
|
||||||
|
// If the Nav Host fragment exists, return it
|
||||||
|
val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
|
||||||
|
existingFragment?.let { return it }
|
||||||
|
|
||||||
|
// Otherwise, create it and return it.
|
||||||
|
val navHostFragment = NavHostFragment.create(navGraphId)
|
||||||
|
fragmentManager.beginTransaction()
|
||||||
|
.add(containerId, navHostFragment, fragmentTag)
|
||||||
|
.commitNow()
|
||||||
|
return navHostFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FragmentManager.isOnBackStack(backStackName: String): Boolean {
|
||||||
|
val backStackCount = backStackEntryCount
|
||||||
|
for (index in 0 until backStackCount) {
|
||||||
|
if (getBackStackEntryAt(index).name == backStackName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFragmentTag(index: Int) = "bottomNavigation#$index"
|
||||||
|
*/
|
||||||
@@ -27,4 +27,7 @@ abstract class BaseVM : ViewModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } }
|
}.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } }
|
||||||
|
|
||||||
|
protected suspend fun <T : VKEvent> sendEvent(event: T) = tasksEventChannel.send(event)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,3 +6,6 @@ data class ShowDialogInfoEvent(
|
|||||||
val positiveBtn: String? = null,
|
val positiveBtn: String? = null,
|
||||||
val negativeBtn: String? = null
|
val negativeBtn: String? = null
|
||||||
) : VKEvent()
|
) : VKEvent()
|
||||||
|
|
||||||
|
object StartProgressEvent : VKEvent()
|
||||||
|
object StopProgressEvent : VKEvent()
|
||||||
@@ -4,6 +4,7 @@ import android.content.Intent
|
|||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import androidx.core.util.forEach
|
import androidx.core.util.forEach
|
||||||
import androidx.core.util.set
|
import androidx.core.util.set
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
@@ -11,6 +12,7 @@ import androidx.navigation.NavController
|
|||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
|
import com.meloda.fast.UserConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the various graphs needed for a [BottomNavigationView].
|
* Manages the various graphs needed for a [BottomNavigationView].
|
||||||
@@ -22,7 +24,6 @@ object NavigationExtensions {
|
|||||||
fun BottomNavigationView.setupWithNavController(
|
fun BottomNavigationView.setupWithNavController(
|
||||||
navGraphIds: List<Int>,
|
navGraphIds: List<Int>,
|
||||||
fragmentManager: FragmentManager,
|
fragmentManager: FragmentManager,
|
||||||
// dataManager: DataManager,
|
|
||||||
containerId: Int,
|
containerId: Int,
|
||||||
intent: Intent
|
intent: Intent
|
||||||
): LiveData<NavController> {
|
): LiveData<NavController> {
|
||||||
@@ -71,8 +72,7 @@ object NavigationExtensions {
|
|||||||
val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
|
val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
|
||||||
var isOnFirstFragment = selectedItemTag == firstFragmentTag
|
var isOnFirstFragment = selectedItemTag == firstFragmentTag
|
||||||
|
|
||||||
// When a navigation item is selected
|
setOnItemSelectedListener { item ->
|
||||||
setOnNavigationItemSelectedListener { item ->
|
|
||||||
// Don't do anything if the state is state has already been saved.
|
// Don't do anything if the state is state has already been saved.
|
||||||
if (fragmentManager.isStateSaved) {
|
if (fragmentManager.isStateSaved) {
|
||||||
false
|
false
|
||||||
@@ -81,12 +81,9 @@ object NavigationExtensions {
|
|||||||
(fragmentManager.findFragmentByTag(selectedItemTag) as NavHostFragment).navController
|
(fragmentManager.findFragmentByTag(selectedItemTag) as NavHostFragment).navController
|
||||||
navController.popBackStack(navController.graph.startDestination, false)
|
navController.popBackStack(navController.graph.startDestination, false)
|
||||||
if (selectedItemTag != graphIdToTagMap[item.itemId]) {
|
if (selectedItemTag != graphIdToTagMap[item.itemId]) {
|
||||||
// val listCheck = listOf(R.id.contacts, R.id.chats)
|
val newlySelectedItemTag = //graphIdToTagMap[item.itemId]
|
||||||
val newlySelectedItemTag =
|
if (!UserConfig.isLoggedIn()) graphIdToTagMap[R.id.login] else graphIdToTagMap[item.itemId]
|
||||||
//if (listCheck.contains(item.itemId) && dataManager.token.isBlank()) graphIdToTagMap[R.id.signIn] else
|
|
||||||
graphIdToTagMap[item.itemId]
|
|
||||||
|
|
||||||
// Pop everything above the first fragment (the "fixed start destination")
|
|
||||||
fragmentManager.popBackStack(
|
fragmentManager.popBackStack(
|
||||||
firstFragmentTag,
|
firstFragmentTag,
|
||||||
FragmentManager.POP_BACK_STACK_INCLUSIVE
|
FragmentManager.POP_BACK_STACK_INCLUSIVE
|
||||||
@@ -128,7 +125,8 @@ object NavigationExtensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setOnNavigationItemReselectedListener { item ->
|
|
||||||
|
setOnItemReselectedListener { item ->
|
||||||
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
|
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
|
||||||
val selectedFragment =
|
val selectedFragment =
|
||||||
fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment
|
fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment
|
||||||
@@ -257,5 +255,12 @@ object NavigationExtensions {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val FragmentManager.visibleFragments
|
||||||
|
get(): List<Fragment> {
|
||||||
|
val visibleFragments = arrayListOf<Fragment>()
|
||||||
|
fragments.forEach { if (it.isVisible) visibleFragments.add(it) }
|
||||||
|
return visibleFragments
|
||||||
|
}
|
||||||
|
|
||||||
private fun getFragmentTag(index: Int) = "bottomNavigation#$index"
|
private fun getFragmentTag(index: Int) = "bottomNavigation#$index"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.meloda.fast.fragment.friends
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
|
import com.meloda.fast.R
|
||||||
|
import com.meloda.fast.base.BaseFragment
|
||||||
|
import com.meloda.fast.databinding.FragmentFriendsBinding
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class FriendsFragment : BaseFragment(R.layout.fragment_friends) {
|
||||||
|
|
||||||
|
private val binding: FragmentFriendsBinding by viewBinding()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.meloda.fast.fragment.important
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
|
import com.meloda.fast.R
|
||||||
|
import com.meloda.fast.base.BaseFragment
|
||||||
|
import com.meloda.fast.databinding.FragmentImportantBinding
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ImportantFragment : BaseFragment(R.layout.fragment_important) {
|
||||||
|
|
||||||
|
private val binding: FragmentImportantBinding by viewBinding()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.meloda.fast.fragment.login
|
package com.meloda.fast.fragment.login
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
@@ -7,9 +8,11 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.viewbinding.library.fragment.viewBinding
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
|
import android.webkit.CookieManager
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.fragment.app.setFragmentResultListener
|
import androidx.fragment.app.setFragmentResultListener
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
@@ -21,11 +24,15 @@ import com.google.android.material.textfield.TextInputEditText
|
|||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
import com.meloda.fast.base.BaseVMFragment
|
import com.meloda.fast.base.BaseVMFragment
|
||||||
|
import com.meloda.fast.base.viewmodel.StartProgressEvent
|
||||||
|
import com.meloda.fast.base.viewmodel.StopProgressEvent
|
||||||
import com.meloda.fast.base.viewmodel.VKEvent
|
import com.meloda.fast.base.viewmodel.VKEvent
|
||||||
import com.meloda.fast.databinding.FragmentLoginBinding
|
import com.meloda.fast.databinding.FragmentLoginBinding
|
||||||
|
import com.meloda.fast.fragment.main.MainFragment
|
||||||
import com.meloda.fast.util.KeyboardUtils
|
import com.meloda.fast.util.KeyboardUtils
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
@@ -45,7 +52,7 @@ class LoginFragment : BaseVMFragment<LoginVM>(R.layout.fragment_login) {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel.checkUserSession()
|
(parentFragment?.parentFragment as? MainFragment)?.bottomBar?.isVisible = false
|
||||||
|
|
||||||
prepareViews()
|
prepareViews()
|
||||||
|
|
||||||
@@ -63,16 +70,49 @@ class LoginFragment : BaseVMFragment<LoginVM>(R.layout.fragment_login) {
|
|||||||
when (event) {
|
when (event) {
|
||||||
is ShowCaptchaDialog -> showCaptchaDialog(event.captchaImage, event.captchaSid)
|
is ShowCaptchaDialog -> showCaptchaDialog(event.captchaImage, event.captchaSid)
|
||||||
is GoToValidationEvent -> goToValidation(event.redirectUrl)
|
is GoToValidationEvent -> goToValidation(event.redirectUrl)
|
||||||
GoToMainEvent -> goToMain()
|
is GoToMainEvent -> goToMain(event.haveAuthorized)
|
||||||
|
StartProgressEvent -> onProgressStarted()
|
||||||
|
StopProgressEvent -> onProgressEnded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onProgressStarted() {
|
||||||
|
binding.loginContainer.isVisible = false
|
||||||
|
binding.passwordContainer.isVisible = false
|
||||||
|
binding.auth.isVisible = false
|
||||||
|
binding.progress.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onProgressEnded() {
|
||||||
|
binding.loginContainer.isVisible = true
|
||||||
|
binding.passwordContainer.isVisible = true
|
||||||
|
binding.auth.isVisible = true
|
||||||
|
binding.progress.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
private fun prepareViews() {
|
private fun prepareViews() {
|
||||||
|
prepareWebView()
|
||||||
prepareEmailEditText()
|
prepareEmailEditText()
|
||||||
preparePasswordEditText()
|
preparePasswordEditText()
|
||||||
prepareAuthButton()
|
prepareAuthButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
private fun prepareWebView() {
|
||||||
|
with(binding.webView) {
|
||||||
|
settings.javaScriptEnabled = true
|
||||||
|
settings.domStorageEnabled = true
|
||||||
|
settings.loadsImagesAutomatically = false
|
||||||
|
settings.userAgentString = "Chrome/41.0.2228.0 Safari/537.36"
|
||||||
|
clearCache(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cookieManager = CookieManager.getInstance()
|
||||||
|
cookieManager.removeAllCookies(null)
|
||||||
|
cookieManager.flush()
|
||||||
|
cookieManager.setAcceptCookie(false)
|
||||||
|
}
|
||||||
|
|
||||||
private fun prepareEmailEditText() {
|
private fun prepareEmailEditText() {
|
||||||
binding.loginInput.addTextChangedListener {
|
binding.loginInput.addTextChangedListener {
|
||||||
if (!binding.loginLayout.error.isNullOrBlank()) binding.loginLayout.error = ""
|
if (!binding.loginLayout.error.isNullOrBlank()) binding.loginLayout.error = ""
|
||||||
@@ -98,21 +138,30 @@ class LoginFragment : BaseVMFragment<LoginVM>(R.layout.fragment_login) {
|
|||||||
(event.action == KeyEvent.ACTION_DOWN && (event.keyCode == KeyEvent.KEYCODE_ENTER || event.keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER))
|
(event.action == KeyEvent.ACTION_DOWN && (event.keyCode == KeyEvent.KEYCODE_ENTER || event.keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER))
|
||||||
) {
|
) {
|
||||||
KeyboardUtils.hideKeyboardFrom(binding.passwordInput)
|
KeyboardUtils.hideKeyboardFrom(binding.passwordInput)
|
||||||
binding.authorize.performClick()
|
binding.auth.performClick()
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareAuthButton() {
|
private fun prepareAuthButton() {
|
||||||
binding.authorize.setOnClickListener {
|
binding.auth.setOnClickListener {
|
||||||
|
if (binding.progress.isVisible) return@setOnClickListener
|
||||||
|
|
||||||
val loginString = binding.loginInput.text.toString().trim()
|
val loginString = binding.loginInput.text.toString().trim()
|
||||||
val passwordString = binding.passwordInput.text.toString().trim()
|
val passwordString = binding.passwordInput.text.toString().trim()
|
||||||
|
|
||||||
if (!validateInputData(loginString, passwordString)) return@setOnClickListener
|
if (!validateInputData(loginString, passwordString)) return@setOnClickListener
|
||||||
|
|
||||||
viewModel.login(requireContext(), loginString, passwordString)
|
|
||||||
KeyboardUtils.hideKeyboardFrom(it)
|
KeyboardUtils.hideKeyboardFrom(it)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.login(
|
||||||
|
binding.webView,
|
||||||
|
loginString,
|
||||||
|
passwordString
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,17 +242,18 @@ class LoginFragment : BaseVMFragment<LoginVM>(R.layout.fragment_login) {
|
|||||||
builder.setPositiveButton(android.R.string.ok) { _, _ ->
|
builder.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
val captchaCode = captchaCodeEditText.text.toString().trim()
|
val captchaCode = captchaCodeEditText.text.toString().trim()
|
||||||
|
|
||||||
viewModel.login(
|
lifecycleScope.launch {
|
||||||
requireContext(),
|
viewModel.login(
|
||||||
lastEmail,
|
binding.webView,
|
||||||
lastPassword,
|
lastEmail,
|
||||||
"&captcha_sid=$captchaSid&captcha_key=$captchaCode"
|
lastPassword,
|
||||||
)
|
"&captcha_sid=$captchaSid&captcha_key=$captchaCode"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setTitle(R.string.input_captcha)
|
builder.setTitle(R.string.input_captcha)
|
||||||
builder.show()
|
builder.show()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToValidation(redirectUrl: String) {
|
private fun goToValidation(redirectUrl: String) {
|
||||||
@@ -213,8 +263,12 @@ class LoginFragment : BaseVMFragment<LoginVM>(R.layout.fragment_login) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToMain() {
|
private fun goToMain(haveAuthorized: Boolean) {
|
||||||
findNavController().navigate(R.id.toMain)
|
lifecycleScope.launch {
|
||||||
|
if (haveAuthorized) delay(500)
|
||||||
|
|
||||||
|
findNavController().navigate(R.id.toMain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
package com.meloda.fast.fragment.login
|
package com.meloda.fast.fragment.login
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.webkit.CookieManager
|
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
@@ -12,68 +8,53 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.meloda.fast.UserConfig
|
import com.meloda.fast.UserConfig
|
||||||
import com.meloda.fast.api.VKAuth
|
import com.meloda.fast.api.VKAuth
|
||||||
import com.meloda.fast.base.viewmodel.BaseVM
|
import com.meloda.fast.base.viewmodel.BaseVM
|
||||||
|
import com.meloda.fast.base.viewmodel.StartProgressEvent
|
||||||
|
import com.meloda.fast.base.viewmodel.StopProgressEvent
|
||||||
import com.meloda.fast.base.viewmodel.VKEvent
|
import com.meloda.fast.base.viewmodel.VKEvent
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
class LoginVM : BaseVM() {
|
class LoginVM : BaseVM() {
|
||||||
|
|
||||||
fun login(
|
private var isWebViewPrepared = true
|
||||||
context: Context,
|
|
||||||
|
suspend fun login(
|
||||||
|
webView: WebView,
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
captcha: String = ""
|
captcha: String = ""
|
||||||
) {
|
) {
|
||||||
|
sendEvent(StartProgressEvent)
|
||||||
|
|
||||||
val urlToGo = VKAuth.getDirectAuthUrl(email, password, captcha)
|
val urlToGo = VKAuth.getDirectAuthUrl(email, password, captcha)
|
||||||
|
|
||||||
// val builder = AlertDialog.Builder(context)
|
if (isWebViewPrepared) {
|
||||||
|
isWebViewPrepared = false
|
||||||
|
|
||||||
val webView = createWebView(context)
|
webView.addJavascriptInterface(WebViewHandlerInterface(), "HtmlHandler")
|
||||||
webView.layoutParams = ViewGroup.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
||||||
)
|
|
||||||
|
|
||||||
// builder.setTitle("Auth")
|
webView.webViewClient = object : WebViewClient() {
|
||||||
// builder.setView(webView)
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
// builder.show()
|
webView.loadUrl(
|
||||||
|
"javascript:window.HtmlHandler.handleHtml" +
|
||||||
webView.addJavascriptInterface(WebViewHandlerInterface(), "HtmlHandler")
|
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');"
|
||||||
webView.webViewClient = object : WebViewClient() {
|
)
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
}
|
||||||
webView.loadUrl(
|
|
||||||
"javascript:window.HtmlHandler.handleHtml" +
|
|
||||||
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
webView.loadUrl(urlToGo)
|
webView.loadUrl(urlToGo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
private fun createWebView(context: Context): WebView {
|
|
||||||
val loginWebView = WebView(context)
|
|
||||||
|
|
||||||
loginWebView.settings.javaScriptEnabled = true
|
|
||||||
loginWebView.settings.domStorageEnabled = true
|
|
||||||
loginWebView.settings.loadsImagesAutomatically = false
|
|
||||||
loginWebView.settings.userAgentString = "Chrome/41.0.2228.0 Safari/537.36"
|
|
||||||
|
|
||||||
loginWebView.clearCache(true)
|
|
||||||
|
|
||||||
val cookieManager = CookieManager.getInstance()
|
|
||||||
cookieManager.removeAllCookies(null)
|
|
||||||
cookieManager.flush()
|
|
||||||
cookieManager.setAcceptCookie(false)
|
|
||||||
|
|
||||||
return loginWebView
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("MoveVariableDeclarationIntoWhen")
|
@Suppress("MoveVariableDeclarationIntoWhen")
|
||||||
private fun checkResponse(response: JSONObject) {
|
private fun checkResponse(response: JSONObject) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
delay(1500)
|
||||||
|
sendEvent(StopProgressEvent)
|
||||||
|
|
||||||
if (response.has("error")) {
|
if (response.has("error")) {
|
||||||
val errorString = response.optString("error")
|
val errorString = response.optString("error")
|
||||||
|
|
||||||
@@ -82,33 +63,12 @@ class LoginVM : BaseVM() {
|
|||||||
val redirectUrl = response.optString("redirect_uri")
|
val redirectUrl = response.optString("redirect_uri")
|
||||||
|
|
||||||
tasksEventChannel.send(GoToValidationEvent(redirectUrl))
|
tasksEventChannel.send(GoToValidationEvent(redirectUrl))
|
||||||
|
|
||||||
// val bundle = Bundle()
|
|
||||||
// bundle.putString("url", redirectUrl)
|
|
||||||
|
|
||||||
/* fragment.setFragmentResultListener("validation") { _, bundle ->
|
|
||||||
val userId = bundle.getInt("userId")
|
|
||||||
val token = bundle.getString("token") ?: ""
|
|
||||||
saveUserData(userId, token)
|
|
||||||
|
|
||||||
openMainScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fragment.parentFragmentManager.beginTransaction()
|
|
||||||
.replace(
|
|
||||||
R.id.fragmentContainer,
|
|
||||||
ValidationFragment().apply { arguments = bundle })
|
|
||||||
.addToBackStack("")
|
|
||||||
.commit()*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
"need_captcha" -> {
|
"need_captcha" -> {
|
||||||
val captchaImage = response.optString("captcha_img")
|
val captchaImage = response.optString("captcha_img")
|
||||||
val captchaSid = response.optString("captcha_sid")
|
val captchaSid = response.optString("captcha_sid")
|
||||||
|
|
||||||
tasksEventChannel.send(ShowCaptchaDialog(captchaImage, captchaSid))
|
tasksEventChannel.send(ShowCaptchaDialog(captchaImage, captchaSid))
|
||||||
// showCaptchaDialog(captchaImage, captchaSid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -118,11 +78,7 @@ class LoginVM : BaseVM() {
|
|||||||
UserConfig.accessToken = accessToken
|
UserConfig.accessToken = accessToken
|
||||||
UserConfig.userId = userId
|
UserConfig.userId = userId
|
||||||
|
|
||||||
tasksEventChannel.send(GoToMainEvent)
|
tasksEventChannel.send(GoToMainEvent())
|
||||||
|
|
||||||
// openMainScreen()
|
|
||||||
|
|
||||||
// onResponseListener?.onResponse(null)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,11 +90,7 @@ class LoginVM : BaseVM() {
|
|||||||
UserConfig.accessToken = accessToken
|
UserConfig.accessToken = accessToken
|
||||||
UserConfig.userId = userId
|
UserConfig.userId = userId
|
||||||
|
|
||||||
tasksEventChannel.send(GoToMainEvent)
|
tasksEventChannel.send(GoToMainEvent())
|
||||||
}
|
|
||||||
|
|
||||||
fun checkUserSession() = viewModelScope.launch {
|
|
||||||
if (UserConfig.isLoggedIn()) tasksEventChannel.send(GoToMainEvent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class WebViewHandlerInterface {
|
inner class WebViewHandlerInterface {
|
||||||
@@ -158,4 +110,4 @@ class LoginVM : BaseVM() {
|
|||||||
|
|
||||||
data class ShowCaptchaDialog(val captchaImage: String, val captchaSid: String) : VKEvent()
|
data class ShowCaptchaDialog(val captchaImage: String, val captchaSid: String) : VKEvent()
|
||||||
data class GoToValidationEvent(val redirectUrl: String) : VKEvent()
|
data class GoToValidationEvent(val redirectUrl: String) : VKEvent()
|
||||||
object GoToMainEvent : VKEvent()
|
data class GoToMainEvent(val haveAuthorized: Boolean = true) : VKEvent()
|
||||||
@@ -4,7 +4,9 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.viewbinding.library.fragment.viewBinding
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
|
import com.meloda.fast.UserConfig
|
||||||
import com.meloda.fast.base.BaseVMFragment
|
import com.meloda.fast.base.BaseVMFragment
|
||||||
import com.meloda.fast.databinding.FragmentMainBinding
|
import com.meloda.fast.databinding.FragmentMainBinding
|
||||||
import com.meloda.fast.extensions.NavigationExtensions.setupWithNavController
|
import com.meloda.fast.extensions.NavigationExtensions.setupWithNavController
|
||||||
@@ -21,18 +23,29 @@ class MainFragment : BaseVMFragment<MainVM>(R.layout.fragment_main) {
|
|||||||
|
|
||||||
if (savedInstanceState == null) setupBottomBar()
|
if (savedInstanceState == null) setupBottomBar()
|
||||||
|
|
||||||
|
if (!UserConfig.isLoggedIn()) findNavController().navigate(R.id.toLogin)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBottomBar() {
|
private fun setupBottomBar() {
|
||||||
val navGraphIds = listOf(R.id.messages, R.id.friends)
|
val navGraphIds = listOf(
|
||||||
|
R.navigation.messages,
|
||||||
binding.bottomBar.setupWithNavController(
|
R.navigation.friends,
|
||||||
navGraphIds = listOf(),
|
R.navigation.important,
|
||||||
fragmentManager = childFragmentManager,
|
R.navigation.login
|
||||||
containerId = R.id.fragmentContainer,
|
|
||||||
intent = requireActivity().intent
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with(binding.bottomBar) {
|
||||||
|
selectedItemId = R.id.messages
|
||||||
|
setupWithNavController(
|
||||||
|
navGraphIds = navGraphIds,
|
||||||
|
fragmentManager = childFragmentManager,
|
||||||
|
containerId = R.id.fragmentContainer,
|
||||||
|
intent = requireActivity().intent
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val bottomBar get() = binding.bottomBar
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,21 @@
|
|||||||
package com.meloda.fast.fragment.messages
|
package com.meloda.fast.fragment.messages
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.viewbinding.library.fragment.viewBinding
|
||||||
import com.meloda.fast.R
|
import com.meloda.fast.R
|
||||||
import com.meloda.fast.base.BaseFragment
|
import com.meloda.fast.base.BaseFragment
|
||||||
|
import com.meloda.fast.databinding.FragmentConversationsBinding
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ConversationsFragment : BaseFragment(R.layout.fragment_conversations) {
|
class ConversationsFragment : BaseFragment(R.layout.fragment_conversations) {
|
||||||
|
|
||||||
|
private val binding: FragmentConversationsBinding by viewBinding()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
|
||||||
|
</vector>
|
||||||
@@ -4,6 +4,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="conversations" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -5,20 +5,25 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="friends" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
app:elevation="0dp">
|
app:elevation="0dp">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:background="@drawable/toolbar_background"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/toolbar_background"
|
||||||
app:layout_scrollFlags="scroll|enterAlways">
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
<include layout="@layout/toolbar" />
|
<include layout="@layout/toolbar" />
|
||||||
|
|||||||
@@ -3,4 +3,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="important" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:id="@+id/loginRoot"
|
android:id="@+id/loginRoot"
|
||||||
@@ -8,6 +9,12 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/webView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -16,6 +23,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
@@ -37,6 +45,14 @@
|
|||||||
app:tint="?colorAccent" />
|
app:tint="?colorAccent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="48dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/loginContainer"
|
android:id="@+id/loginContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -110,7 +126,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/authorize"
|
android:id="@+id/auth"
|
||||||
style="@style/Widget.MaterialButton"
|
style="@style/Widget.MaterialButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigationFriends"
|
android:id="@id/friends"
|
||||||
android:icon="@drawable/ic_people_outline"
|
android:icon="@drawable/ic_people_outline"
|
||||||
android:title="@string/navigation_friends" />
|
android:title="@string/navigation_friends" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigationConversations"
|
android:id="@id/messages"
|
||||||
android:icon="@drawable/ic_message_outline"
|
android:icon="@drawable/ic_message_outline"
|
||||||
android:title="@string/navigation_chats" />
|
android:title="@string/navigation_chats" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigationImportant"
|
android:id="@id/important"
|
||||||
android:icon="@drawable/ic_star_border"
|
android:icon="@drawable/ic_star_border"
|
||||||
android:title="@string/navigation_important" />
|
android:title="@string/navigation_important" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/friends">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/friends"
|
||||||
|
app:startDestination="@+id/friendsFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/friendsFragment"
|
||||||
|
android:name="com.meloda.fast.fragment.friends.FriendsFragment"
|
||||||
|
android:label="FriendsFragment"
|
||||||
|
tools:layout="@layout/fragment_friends" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation 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:id="@+id/important"
|
||||||
|
app:startDestination="@id/importantFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/importantFragment"
|
||||||
|
android:name="com.meloda.fast.fragment.important.ImportantFragment"
|
||||||
|
android:label="ImportantFragment"
|
||||||
|
tools:layout="@layout/fragment_important" />
|
||||||
|
|
||||||
|
</navigation>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation 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:id="@+id/login"
|
||||||
|
app:startDestination="@id/loginFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/loginFragment"
|
||||||
|
android:name="com.meloda.fast.fragment.login.LoginFragment"
|
||||||
|
android:label="LoginFragment"
|
||||||
|
tools:layout="@layout/fragment_login">
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/toMain"
|
||||||
|
app:destination="@id/mainFragment"
|
||||||
|
app:popUpTo="@id/loginFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/toValidation"
|
||||||
|
app:destination="@id/validationFragment" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/validationFragment"
|
||||||
|
android:name="com.meloda.fast.fragment.login.ValidationFragment"
|
||||||
|
android:label="ValidationFragment"
|
||||||
|
tools:layout="@layout/fragment_validation">
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/toLogin"
|
||||||
|
app:destination="@id/loginFragment"
|
||||||
|
app:popUpTo="@id/validationFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
|
||||||
|
</navigation>
|
||||||
@@ -3,13 +3,19 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/main"
|
android:id="@+id/main"
|
||||||
app:startDestination="@id/loginFragment">
|
app:startDestination="@id/mainFragment">
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/mainFragment"
|
android:id="@+id/mainFragment"
|
||||||
android:name="com.meloda.fast.fragment.main.MainFragment"
|
android:name="com.meloda.fast.fragment.main.MainFragment"
|
||||||
android:label="MainFragment"
|
android:label="MainFragment"
|
||||||
tools:layout="@layout/fragment_main" />
|
tools:layout="@layout/fragment_main">
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/toLogin"
|
||||||
|
app:destination="@id/loginFragment" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/loginFragment"
|
android:id="@+id/loginFragment"
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/messages">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/messages"
|
||||||
|
app:startDestination="@+id/conversationsFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/conversationsFragment"
|
||||||
|
android:name="com.meloda.fast.fragment.messages.ConversationsFragment"
|
||||||
|
android:label="ConversationsFragment"
|
||||||
|
tools:layout="@layout/fragment_conversations" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
|
||||||
|
<item name="colorPrimary">@color/dark_primary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_primaryDark</item>
|
||||||
|
<item name="colorAccent">@color/dark_accent</item>
|
||||||
|
|
||||||
|
<item name="toolbarStyle">@style/AppTheme.Toolbar</item>
|
||||||
|
|
||||||
|
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
||||||
|
<item name="dialogCornerRadius">12dp</item>
|
||||||
|
<item name="android:windowBackground">@color/dark_background</item>
|
||||||
|
<item name="itemTitleColor">?colorAccent</item>
|
||||||
|
<item name="dividerHorizontal">@color/dark_divider</item>
|
||||||
|
<item name="textColorSecondary">@color/dark_textSecondary</item>
|
||||||
|
<item name="editTextFilledBackgroundColor">@color/dark_edittext_filled_background</item>
|
||||||
|
|
||||||
|
<item name="messageInTextColor">@color/dark_message_in</item>
|
||||||
|
<item name="messageOutTextColor">@color/dark_message_out</item>
|
||||||
|
|
||||||
|
<item name="android:windowAnimationStyle">@style/AppTheme.ActivityAnimation</item>
|
||||||
|
|
||||||
|
<item name="android:navigationBarColor">@color/dark_navigationBar</item>
|
||||||
|
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
||||||
|
@android:color/transparent
|
||||||
|
</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Toolbar" parent="Widget.MaterialComponents.Toolbar.PrimarySurface">
|
||||||
|
<item name="titleTextAppearance">@style/Toolbar.Title</item>
|
||||||
|
<item name="android:textSize">24sp</item>
|
||||||
|
<item name="android:elevation">3dp</item>
|
||||||
|
<item name="titleTextColor">@color/dark_accent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Login.EditText" parent="">
|
||||||
|
<item name="android:layout_height">52dp</item>
|
||||||
|
<item name="android:background">@drawable/edittext_filled_background</item>
|
||||||
|
<item name="android:paddingStart">16dp</item>
|
||||||
|
<item name="android:paddingEnd">16dp</item>
|
||||||
|
<item name="android:layout_marginEnd">16dp</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
|
<item name="android:textColorHint">?textColorSecondary</item>
|
||||||
|
<item name="fontFamily">@font/google_sans_regular</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
<item name="android:maxLines">1</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.FullScreenDialog" parent="Theme.MaterialComponents.DayNight.Dialog.Bridge">
|
||||||
|
<item name="colorPrimary">@color/dark_primary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_primaryDark</item>
|
||||||
|
<item name="colorAccent">@color/dark_accent</item>
|
||||||
|
<item name="android:windowIsFloating">false</item>
|
||||||
|
<item name="android:windowBackground">@color/dark_background</item>
|
||||||
|
<item name="actionMenuTextColor">?colorAccent</item>
|
||||||
|
|
||||||
|
<item name="android:navigationBarColor" tools:targetApi="o_mr1">@color/dark_navigationBar</item>
|
||||||
|
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
||||||
|
@android:color/transparent
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item name="colorControlNormal">?colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.ProfileDialog" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
|
||||||
|
<item name="colorAccent">@color/dark_accent</item>
|
||||||
|
<item name="colorPrimary">@color/dark_primary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_primaryDark</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
@@ -27,8 +27,6 @@
|
|||||||
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
||||||
@android:color/transparent
|
@android:color/transparent
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.Toolbar" parent="Widget.MaterialComponents.Toolbar.PrimarySurface">
|
<style name="AppTheme.Toolbar" parent="Widget.MaterialComponents.Toolbar.PrimarySurface">
|
||||||
@@ -59,15 +57,16 @@
|
|||||||
<item name="android:maxLines">1</item>
|
<item name="android:maxLines">1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.FullScreenDialog" parent="Theme.MaterialComponents.Light.Dialog.Bridge">
|
<style name="AppTheme.FullScreenDialog" parent="Theme.MaterialComponents.DayNight.Dialog.Bridge">
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
<item name="android:windowIsFloating">false</item>
|
<item name="android:windowIsFloating">false</item>
|
||||||
<item name="android:windowBackground">@color/background</item>
|
<item name="android:windowBackground">@color/background</item>
|
||||||
<item name="actionMenuTextColor">?colorAccent</item>
|
<item name="actionMenuTextColor">?colorAccent</item>
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
|
|
||||||
<item name="android:navigationBarColor" tools:targetApi="o_mr1">?colorPrimaryDark</item>
|
<item name="android:navigationBarColor" tools:targetApi="o_mr1">@color/navigationBar</item>
|
||||||
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
||||||
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
<item name="android:navigationBarDividerColor" tools:targetApi="o_mr1">
|
||||||
@android:color/transparent
|
@android:color/transparent
|
||||||
@@ -82,10 +81,11 @@
|
|||||||
<item name="android:layout_marginEnd">12dp</item>
|
<item name="android:layout_marginEnd">12dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.ProfileDialog" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
|
<style name="AppTheme.ProfileDialog" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Widget.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
<style name="Widget.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
|
|||||||
Reference in New Issue
Block a user