Snippets

John OAuthActivity

Created by John De la cruz
@AndroidEntryPoint
open class OAuthActivity :
    DaggerAuthenticatorActivity(),
    OAuthWebviewHelperEvents,
    EventsObserver,
    ConnectivityReceiver.OnConnectivityChangeListener {
    private val timeoutHandler = Handler(Looper.myLooper()!!)
    private var webViewHelper: OAuthWebviewHelper? = null
    private lateinit var binding: ActivityOauthBinding
    private var wasBackgrounded = false
    private var dialogWindowIsVisible = false

    @Inject
    lateinit var versionChecker: VersionChecker

    @Inject
    lateinit var analytics: AnalyticsTracker
    private var connectivityReceiver: ConnectivityReceiver? = null

    private val onBackPressedCallback: OnBackPressedCallback =
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                webViewHelper?.loadLoginPage()
                finishAffinity()
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityOauthBinding.inflate(layoutInflater)
        analytics.logScreen(this, CLASS_NAME)
        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)

        connectivityReceiver = ConnectivityReceiver(this)
        registerReceiver(
            connectivityReceiver,
            IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        )

        if (intent == null || intent.extras == null) {
            Timber.tag("OAuthActivity").e("Bundle missing from OAuthActivity")
            return
        }
        // Getting login options from intent's extras
        val loginOptions = ClientManager.LoginOptions.fromBundle(intent.extras)

        // Setup content view
        setContentView(binding.root)
        EventsObservable.get().notifyEvent(
            EventsObservable.EventType.AuthWebViewCreateComplete,
            binding.activityOauthWebview
        )
        webViewHelper = OAuthWebviewHelper(
            this,
            this,
            loginOptions,
            binding.activityOauthWebview,
            savedInstanceState
        )

        // Let observers know
        EventsObservable.get()
            .notifyEvent(EventsObservable.EventType.LoginActivityCreateComplete, this)

        if (RuntimeConfig.getRuntimeConfig(this)
                .getBoolean(RuntimeConfig.ConfigKey.RequireCertAuth)
        ) {
            val alias =
                RuntimeConfig.getRuntimeConfig(this)
                    .getString(RuntimeConfig.ConfigKey.ManagedAppCertAlias)
            KeyChain.choosePrivateKeyAlias(this, webViewHelper!!, null, null, null, 0, alias)
        } else {
            webViewHelper?.loadLoginPage()
        }

        EventsObservable.get().registerObserver(this)
        binding.activityOauthRegister.setOnClickListener {
            startActivity(
                Intent(
                    this,
                    RegistrationActivity::class.java
                )
            )
        }

        binding.resetPasswordButton.setOnClickListener {
            startActivity(
                Intent(
                    this,
                    PasswordActivity::class.java
                )
            )
        }
        versionChecker.checkForUpdate { updateMessage: String? ->
            if (!BuildConfig.DEBUG) {
                UpdateVersionActivity.startActivityWithMessage(this, updateMessage)
                finish()
            }
        }

        binding.policyLink.setOnClickListener {
            openPolicy()
        }
    }

    private fun openPolicy() {
        startActivity(
            Intent(
                Intent.ACTION_VIEW,
                Uri.parse(
                    setLink("https://md.com/privacy-policy/")
                )
            )
        )
    }

    override fun onResume() {
        super.onResume()
        if (wasBackgrounded) {
            webViewHelper?.clearView()
            webViewHelper?.loadLoginPage()
            wasBackgrounded = false
        }
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        // This allows sub classes to override the behavior by returning false.
        return if (fixBackButtonBehavior(keyCode)) {
            true
        } else {
            super.onKeyDown(keyCode, event)
        }
    }

    private fun fixBackButtonBehavior(keyCode: Int): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            /*
             * If there are no accounts signed in, we need the login screen
             * to go away, and go back to the home screen. However, if the
             * login screen has been brought up from the switcher screen,
             * the back button should take the user back to the previous screen.
             */
            val accMgr = SalesforceSDKManager.getInstance().userAccountManager
            wasBackgrounded = true
            if (accMgr.authenticatedUsers == null) {
                moveTaskToBack(true)
            } else {
                finish()
            }
            return true
        }
        return false
    }

    override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
        super.onSaveInstanceState(outState, outPersistentState)
        webViewHelper?.saveState(outState)
    }

    override fun onDestroy() {
        if (connectivityReceiver != null) {
            unregisterReceiver(connectivityReceiver)
        }
        EventsObservable.get().unregisterObserver(this)
        timeoutHandler.removeCallbacksAndMessages(null)
        super.onDestroy()
    }

    override fun loadingLoginPage(loginUrl: String) {
        timeoutHandler.removeCallbacksAndMessages(null)
        timeoutHandler.postDelayed({ webViewHelper?.loadLoginPage() }, OAUTH_REFRESH_TIMEOUT)
    }

    override fun onAccountAuthenticatorResult(authResult: Bundle) {
        setAccountAuthenticatorResult(authResult)
    }

    override fun finish(userAccount: UserAccount?) {
        try {
            val am = SalesforceAnalyticsManager.getInstance(userAccount)
            am.enableLogging(false)
            val remotesField = SalesforceAnalyticsManager::class.java.getDeclaredField("remotes")
            remotesField.isAccessible = true
            (Objects.requireNonNull(remotesField[am]) as MutableMap<*, *>).clear()
        } catch (e: IOException) {
            Timber.tag("OAuthActivity").e(e)
        } catch (e: URISyntaxException) {
            Timber.tag("URISyntaxException").e(e)
        }

        val userAccountManager = SalesforceSDKManager.getInstance().userAccountManager
        val authenticatedUsers = userAccountManager.authenticatedUsers
        val numAuthenticatedUsers = authenticatedUsers?.size ?: 0

        val userSwitchType: Int = if (numAuthenticatedUsers == 1) {
            // We've already authenticated the first user, so there should be one.
            UserAccountManager.USER_SWITCH_TYPE_FIRST_LOGIN
        } else if (numAuthenticatedUsers > 1) {
            // Otherwise we're logging in with an additional user.
            UserAccountManager.USER_SWITCH_TYPE_LOGIN
        } else {
            // This should never happen but if it does, pass in the "unknown" value.
            UserAccountManager.USER_SWITCH_TYPE_DEFAULT
        }

        val preferences: SharedPreferences =
            this.getSharedPreferences("data", MODE_PRIVATE)

        with(preferences.edit()) {
            putBoolean("user_logged_in", true)
            apply()
        }
        userAccountManager.sendUserSwitchIntent(userSwitchType, null)

        finish()
    }

    override fun onEvent(event: EventsObservable.Event) {
        if (event.type != EventsObservable.EventType.AuthWebViewPageFinished) {
            // Nothing to do
            return
        }

        val url = (event.data as String).lowercase(Locale.getDefault())
        if (url.endsWith(URL_SITE_LOGIN)) {
            webViewHelper?.loadLoginPage()
        }

        val showRegister = url.contains(URL_SITE_LOGIN)
        val builder = AlertDialog.Builder(this, R.style.DialogTheme)

        if (!showRegister &&
            !dialogWindowIsVisible &&
            url.contains(AUTHORIZE)
        ) {
            builder.setTitle(getString(R.string.login_privacy_dialog_title))
            builder.setMessage(getString(R.string.login_privacy_dialog_text))
            builder.setPositiveButton(
                getString(R.string.see_policy_label)
            ) { dialogInterface: DialogInterface, _: Int ->
                openPolicy()
                dialogInterface.dismiss()
                dialogWindowIsVisible = false
            }
            builder.setNegativeButton(
                getString(R.string.acknowledge_label)
            ) { dialogInterface: DialogInterface, _: Int ->
                dialogInterface.dismiss()
                dialogWindowIsVisible = false
            }
            builder.show()
            dialogWindowIsVisible = true
        }

        binding.activityOauthRegisterContainer.animate()
            .translationY(if (showRegister) 1f else binding.activityOauthRegisterContainer.height.toFloat())
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    binding.activityOauthRegisterContainer.visibility =
                        if (showRegister) View.VISIBLE else View.GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    binding.activityOauthRegisterContainer.visibility =
                        if (showRegister) View.VISIBLE else View.GONE
                }
            })
    }

    companion object {
        private const val URL_SITE_LOGIN = "sitelogin"
        private const val AUTHORIZE = "secur/frontdoor.jsp"
        private const val CLASS_NAME = "OAuthActivity"

        private val OAUTH_REFRESH_TIMEOUT = TimeUnit.MINUTES.toMillis(1)
    }

    override fun isConnected(isConnected: Boolean) {
        if (isConnected) {
            webViewHelper?.loadLoginPage()
        }
        binding.activityOauthWebview.visibility = if (isConnected) View.VISIBLE else View.INVISIBLE
        binding.noConnectionText.visibility = if (!isConnected) View.VISIBLE else View.INVISIBLE
    }
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.