diff --git a/app/release/app-release.apk b/app/release/app-release.apk deleted file mode 100644 index 6034a02d..00000000 Binary files a/app/release/app-release.apk and /dev/null differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json deleted file mode 100644 index d7d1b834..00000000 --- a/app/release/output-metadata.json +++ /dev/null @@ -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" - } - ] -} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/setupWithNavController.kt b/app/src/main/java/com/meloda/fast/base/setupWithNavController.kt new file mode 100644 index 00000000..60876d47 --- /dev/null +++ b/app/src/main/java/com/meloda/fast/base/setupWithNavController.kt @@ -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, + fragmentManager: FragmentManager, + dataManager: DataManager, + containerId: Int, + intent: Intent +): LiveData { + + // Map of tags + val graphIdToTagMap = SparseArray() + // Result. Mutable live data with the selected controlled + val selectedNavController = MutableLiveData() + + 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, + 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" +*/ \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/viewmodel/BaseVM.kt b/app/src/main/java/com/meloda/fast/base/viewmodel/BaseVM.kt index 55e7a233..69b4a8f5 100644 --- a/app/src/main/java/com/meloda/fast/base/viewmodel/BaseVM.kt +++ b/app/src/main/java/com/meloda/fast/base/viewmodel/BaseVM.kt @@ -27,4 +27,7 @@ abstract class BaseVM : ViewModel() { ) } }.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } } + + protected suspend fun sendEvent(event: T) = tasksEventChannel.send(event) + } \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/base/viewmodel/Events.kt b/app/src/main/java/com/meloda/fast/base/viewmodel/Events.kt index a8b06a79..dfa637b3 100644 --- a/app/src/main/java/com/meloda/fast/base/viewmodel/Events.kt +++ b/app/src/main/java/com/meloda/fast/base/viewmodel/Events.kt @@ -5,4 +5,7 @@ data class ShowDialogInfoEvent( val message: String, val positiveBtn: String? = null, val negativeBtn: String? = null -) : VKEvent() \ No newline at end of file +) : VKEvent() + +object StartProgressEvent : VKEvent() +object StopProgressEvent : VKEvent() \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/extensions/NavigationExtensions.kt b/app/src/main/java/com/meloda/fast/extensions/NavigationExtensions.kt index 85b33033..48a783b8 100644 --- a/app/src/main/java/com/meloda/fast/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/meloda/fast/extensions/NavigationExtensions.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.util.SparseArray import androidx.core.util.forEach import androidx.core.util.set +import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -11,6 +12,7 @@ import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import com.google.android.material.bottomnavigation.BottomNavigationView import com.meloda.fast.R +import com.meloda.fast.UserConfig /** * Manages the various graphs needed for a [BottomNavigationView]. @@ -22,7 +24,6 @@ object NavigationExtensions { fun BottomNavigationView.setupWithNavController( navGraphIds: List, fragmentManager: FragmentManager, -// dataManager: DataManager, containerId: Int, intent: Intent ): LiveData { @@ -71,8 +72,7 @@ object NavigationExtensions { val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] var isOnFirstFragment = selectedItemTag == firstFragmentTag - // When a navigation item is selected - setOnNavigationItemSelectedListener { item -> + setOnItemSelectedListener { item -> // Don't do anything if the state is state has already been saved. if (fragmentManager.isStateSaved) { false @@ -81,12 +81,9 @@ object NavigationExtensions { (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] + val newlySelectedItemTag = //graphIdToTagMap[item.itemId] + if (!UserConfig.isLoggedIn()) graphIdToTagMap[R.id.login] else graphIdToTagMap[item.itemId] - // Pop everything above the first fragment (the "fixed start destination") fragmentManager.popBackStack( firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE @@ -128,7 +125,8 @@ object NavigationExtensions { } } } - setOnNavigationItemReselectedListener { item -> + + setOnItemReselectedListener { item -> val newlySelectedItemTag = graphIdToTagMap[item.itemId] val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment @@ -257,5 +255,12 @@ object NavigationExtensions { return false } + val FragmentManager.visibleFragments + get(): List { + val visibleFragments = arrayListOf() + fragments.forEach { if (it.isVisible) visibleFragments.add(it) } + return visibleFragments + } + private fun getFragmentTag(index: Int) = "bottomNavigation#$index" } \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/friends/FriendsFragment.kt b/app/src/main/java/com/meloda/fast/fragment/friends/FriendsFragment.kt new file mode 100644 index 00000000..4fa16732 --- /dev/null +++ b/app/src/main/java/com/meloda/fast/fragment/friends/FriendsFragment.kt @@ -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) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/important/ImportantFragment.kt b/app/src/main/java/com/meloda/fast/fragment/important/ImportantFragment.kt new file mode 100644 index 00000000..ff009428 --- /dev/null +++ b/app/src/main/java/com/meloda/fast/fragment/important/ImportantFragment.kt @@ -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) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/login/LoginFragment.kt b/app/src/main/java/com/meloda/fast/fragment/login/LoginFragment.kt index 2ccd8b43..8ab73c0e 100644 --- a/app/src/main/java/com/meloda/fast/fragment/login/LoginFragment.kt +++ b/app/src/main/java/com/meloda/fast/fragment/login/LoginFragment.kt @@ -1,5 +1,6 @@ package com.meloda.fast.fragment.login +import android.annotation.SuppressLint import android.os.Bundle import android.view.Gravity import android.view.KeyEvent @@ -7,9 +8,11 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.viewbinding.library.fragment.viewBinding +import android.webkit.CookieManager import android.widget.LinearLayout import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf +import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener import androidx.fragment.app.setFragmentResultListener 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.meloda.fast.R 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.databinding.FragmentLoginBinding +import com.meloda.fast.fragment.main.MainFragment import com.meloda.fast.util.KeyboardUtils import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.* import kotlin.concurrent.schedule @@ -45,7 +52,7 @@ class LoginFragment : BaseVMFragment(R.layout.fragment_login) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.checkUserSession() + (parentFragment?.parentFragment as? MainFragment)?.bottomBar?.isVisible = false prepareViews() @@ -63,16 +70,49 @@ class LoginFragment : BaseVMFragment(R.layout.fragment_login) { when (event) { is ShowCaptchaDialog -> showCaptchaDialog(event.captchaImage, event.captchaSid) 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() { + prepareWebView() prepareEmailEditText() preparePasswordEditText() 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() { binding.loginInput.addTextChangedListener { if (!binding.loginLayout.error.isNullOrBlank()) binding.loginLayout.error = "" @@ -98,21 +138,30 @@ class LoginFragment : BaseVMFragment(R.layout.fragment_login) { (event.action == KeyEvent.ACTION_DOWN && (event.keyCode == KeyEvent.KEYCODE_ENTER || event.keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER)) ) { KeyboardUtils.hideKeyboardFrom(binding.passwordInput) - binding.authorize.performClick() + binding.auth.performClick() true } else false } } private fun prepareAuthButton() { - binding.authorize.setOnClickListener { + binding.auth.setOnClickListener { + if (binding.progress.isVisible) return@setOnClickListener + val loginString = binding.loginInput.text.toString().trim() val passwordString = binding.passwordInput.text.toString().trim() if (!validateInputData(loginString, passwordString)) return@setOnClickListener - viewModel.login(requireContext(), loginString, passwordString) KeyboardUtils.hideKeyboardFrom(it) + + lifecycleScope.launch { + viewModel.login( + binding.webView, + loginString, + passwordString + ) + } } } @@ -193,17 +242,18 @@ class LoginFragment : BaseVMFragment(R.layout.fragment_login) { builder.setPositiveButton(android.R.string.ok) { _, _ -> val captchaCode = captchaCodeEditText.text.toString().trim() - viewModel.login( - requireContext(), - lastEmail, - lastPassword, - "&captcha_sid=$captchaSid&captcha_key=$captchaCode" - ) + lifecycleScope.launch { + viewModel.login( + binding.webView, + lastEmail, + lastPassword, + "&captcha_sid=$captchaSid&captcha_key=$captchaCode" + ) + } } builder.setTitle(R.string.input_captcha) builder.show() - } private fun goToValidation(redirectUrl: String) { @@ -213,8 +263,12 @@ class LoginFragment : BaseVMFragment(R.layout.fragment_login) { ) } - private fun goToMain() { - findNavController().navigate(R.id.toMain) + private fun goToMain(haveAuthorized: Boolean) { + lifecycleScope.launch { + if (haveAuthorized) delay(500) + + findNavController().navigate(R.id.toMain) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/login/LoginVM.kt b/app/src/main/java/com/meloda/fast/fragment/login/LoginVM.kt index dfeb3a24..330fabdd 100644 --- a/app/src/main/java/com/meloda/fast/fragment/login/LoginVM.kt +++ b/app/src/main/java/com/meloda/fast/fragment/login/LoginVM.kt @@ -1,10 +1,6 @@ package com.meloda.fast.fragment.login -import android.annotation.SuppressLint -import android.content.Context import android.os.Bundle -import android.view.ViewGroup -import android.webkit.CookieManager import android.webkit.JavascriptInterface import android.webkit.WebView import android.webkit.WebViewClient @@ -12,68 +8,53 @@ import androidx.lifecycle.viewModelScope import com.meloda.fast.UserConfig import com.meloda.fast.api.VKAuth 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 kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.json.JSONObject import org.jsoup.Jsoup class LoginVM : BaseVM() { - fun login( - context: Context, + private var isWebViewPrepared = true + + suspend fun login( + webView: WebView, email: String, password: String, captcha: String = "" ) { + sendEvent(StartProgressEvent) + val urlToGo = VKAuth.getDirectAuthUrl(email, password, captcha) -// val builder = AlertDialog.Builder(context) + if (isWebViewPrepared) { + isWebViewPrepared = false - val webView = createWebView(context) - webView.layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + webView.addJavascriptInterface(WebViewHandlerInterface(), "HtmlHandler") -// builder.setTitle("Auth") -// builder.setView(webView) -// builder.show() - - webView.addJavascriptInterface(WebViewHandlerInterface(), "HtmlHandler") - webView.webViewClient = object : WebViewClient() { - override fun onPageFinished(view: WebView?, url: String?) { - webView.loadUrl( - "javascript:window.HtmlHandler.handleHtml" + - "(''+document.getElementsByTagName('html')[0].innerHTML+'');" - ) + webView.webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + webView.loadUrl( + "javascript:window.HtmlHandler.handleHtml" + + "(''+document.getElementsByTagName('html')[0].innerHTML+'');" + ) + } } } 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") private fun checkResponse(response: JSONObject) { - viewModelScope.launch { + viewModelScope.launch(Dispatchers.Default) { + delay(1500) + sendEvent(StopProgressEvent) + if (response.has("error")) { val errorString = response.optString("error") @@ -82,33 +63,12 @@ class LoginVM : BaseVM() { val redirectUrl = response.optString("redirect_uri") 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" -> { val captchaImage = response.optString("captcha_img") val captchaSid = response.optString("captcha_sid") tasksEventChannel.send(ShowCaptchaDialog(captchaImage, captchaSid)) -// showCaptchaDialog(captchaImage, captchaSid) } } } else { @@ -118,11 +78,7 @@ class LoginVM : BaseVM() { UserConfig.accessToken = accessToken UserConfig.userId = userId - tasksEventChannel.send(GoToMainEvent) - -// openMainScreen() - -// onResponseListener?.onResponse(null) + tasksEventChannel.send(GoToMainEvent()) } } } @@ -134,11 +90,7 @@ class LoginVM : BaseVM() { UserConfig.accessToken = accessToken UserConfig.userId = userId - tasksEventChannel.send(GoToMainEvent) - } - - fun checkUserSession() = viewModelScope.launch { - if (UserConfig.isLoggedIn()) tasksEventChannel.send(GoToMainEvent) + tasksEventChannel.send(GoToMainEvent()) } inner class WebViewHandlerInterface { @@ -158,4 +110,4 @@ class LoginVM : BaseVM() { data class ShowCaptchaDialog(val captchaImage: String, val captchaSid: String) : VKEvent() data class GoToValidationEvent(val redirectUrl: String) : VKEvent() -object GoToMainEvent : VKEvent() \ No newline at end of file +data class GoToMainEvent(val haveAuthorized: Boolean = true) : VKEvent() \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/main/MainFragment.kt b/app/src/main/java/com/meloda/fast/fragment/main/MainFragment.kt index cff043a7..b71f0063 100644 --- a/app/src/main/java/com/meloda/fast/fragment/main/MainFragment.kt +++ b/app/src/main/java/com/meloda/fast/fragment/main/MainFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.View import android.viewbinding.library.fragment.viewBinding import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import com.meloda.fast.R +import com.meloda.fast.UserConfig import com.meloda.fast.base.BaseVMFragment import com.meloda.fast.databinding.FragmentMainBinding import com.meloda.fast.extensions.NavigationExtensions.setupWithNavController @@ -21,18 +23,29 @@ class MainFragment : BaseVMFragment(R.layout.fragment_main) { if (savedInstanceState == null) setupBottomBar() + if (!UserConfig.isLoggedIn()) findNavController().navigate(R.id.toLogin) } private fun setupBottomBar() { - val navGraphIds = listOf(R.id.messages, R.id.friends) - - binding.bottomBar.setupWithNavController( - navGraphIds = listOf(), - fragmentManager = childFragmentManager, - containerId = R.id.fragmentContainer, - intent = requireActivity().intent + val navGraphIds = listOf( + R.navigation.messages, + R.navigation.friends, + R.navigation.important, + R.navigation.login ) + + with(binding.bottomBar) { + selectedItemId = R.id.messages + setupWithNavController( + navGraphIds = navGraphIds, + fragmentManager = childFragmentManager, + containerId = R.id.fragmentContainer, + intent = requireActivity().intent + ) + } } + val bottomBar get() = binding.bottomBar + } \ No newline at end of file diff --git a/app/src/main/java/com/meloda/fast/fragment/messages/ConversationsFragment.kt b/app/src/main/java/com/meloda/fast/fragment/messages/ConversationsFragment.kt index c5e276d1..d9d5a21f 100644 --- a/app/src/main/java/com/meloda/fast/fragment/messages/ConversationsFragment.kt +++ b/app/src/main/java/com/meloda/fast/fragment/messages/ConversationsFragment.kt @@ -1,9 +1,21 @@ 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.base.BaseFragment +import com.meloda.fast.databinding.FragmentConversationsBinding import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class ConversationsFragment : BaseFragment(R.layout.fragment_conversations) { + + private val binding: FragmentConversationsBinding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + } + } \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml b/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml new file mode 100644 index 00000000..1fc37dc3 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_baseline_account_circle_24.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_conversations.xml b/app/src/main/res/layout/fragment_conversations.xml index 0ba8b3f0..81454c3e 100644 --- a/app/src/main/res/layout/fragment_conversations.xml +++ b/app/src/main/res/layout/fragment_conversations.xml @@ -4,6 +4,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + diff --git a/app/src/main/res/layout/fragment_important.xml b/app/src/main/res/layout/fragment_important.xml index 7fd71ed8..d5ad0418 100644 --- a/app/src/main/res/layout/fragment_important.xml +++ b/app/src/main/res/layout/fragment_important.xml @@ -3,4 +3,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index bb7ebb67..4aef0c18 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + + @@ -37,6 +45,14 @@ app:tint="?colorAccent" /> + + diff --git a/app/src/main/res/navigation/friends.xml b/app/src/main/res/navigation/friends.xml index c54baa11..c18265b4 100644 --- a/app/src/main/res/navigation/friends.xml +++ b/app/src/main/res/navigation/friends.xml @@ -1,6 +1,14 @@ + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/friends" + app:startDestination="@+id/friendsFragment"> + + \ No newline at end of file diff --git a/app/src/main/res/navigation/important.xml b/app/src/main/res/navigation/important.xml new file mode 100644 index 00000000..14fb1eb5 --- /dev/null +++ b/app/src/main/res/navigation/important.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/login.xml b/app/src/main/res/navigation/login.xml new file mode 100644 index 00000000..d23dfd39 --- /dev/null +++ b/app/src/main/res/navigation/login.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/main.xml b/app/src/main/res/navigation/main.xml index 29701442..c1de44cf 100644 --- a/app/src/main/res/navigation/main.xml +++ b/app/src/main/res/navigation/main.xml @@ -3,13 +3,19 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" - app:startDestination="@id/loginFragment"> + app:startDestination="@id/mainFragment"> + tools:layout="@layout/fragment_main"> + + + + + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/messages" + app:startDestination="@+id/conversationsFragment"> + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..3765ee07 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 3450857f..7647c116 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,7 +1,7 @@ - - -