meili-epfl/Meili

View on GitHub
app/src/main/java/com/github/epfl/meili/profile/ProfileActivity.kt

Summary

Maintainability
B
4 hrs
Test Coverage
A
91%
package com.github.epfl.meili.profile

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.ImageButton
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import com.facebook.*
import com.facebook.login.LoginResult
import com.facebook.login.widget.LoginButton
import com.github.epfl.meili.R
import com.github.epfl.meili.auth.Auth
import com.github.epfl.meili.auth.AuthenticationService
import com.github.epfl.meili.auth.FacebookAuthenticationService
import com.github.epfl.meili.profile.favoritepois.FavoritePoisActivity
import com.github.epfl.meili.profile.friends.FriendsListActivity
import com.github.epfl.meili.profile.myposts.MyPostsActivity
import com.github.epfl.meili.util.UIUtility
import com.github.epfl.meili.util.UserPreferences
import com.github.epfl.meili.util.navigation.HomeActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import de.hdodenhof.circleimageview.CircleImageView


class ProfileActivity : HomeActivity(R.layout.activity_profile, R.id.profile_activity) {
    private val launchGallery = registerForActivityResult(ActivityResultContracts.GetContent())
    { viewModel.loadLocalImage(contentResolver, it) }

    private lateinit var photoView: CircleImageView

    private lateinit var photoEditView: FloatingActionButton
    private lateinit var nameView: TextView
    private lateinit var bioView: TextView
    private lateinit var nameEditView: EditText
    private lateinit var bioEditView: EditText

    private lateinit var profileEditButton: FloatingActionButton
    private lateinit var saveButton: Button
    private lateinit var cancelButton: Button
    private lateinit var seeFriendsButton: ImageButton
    private lateinit var signInButton: Button
    private lateinit var facebookSignInButton: LoginButton
    private lateinit var signOutButton: Button
    private lateinit var postsButton: ImageButton
    private lateinit var favoritePoisButton: ImageButton
    private lateinit var lightDarkModeButton: ImageButton

    private lateinit var signedInView: View
    private lateinit var profileView: View
    private lateinit var profileEditView: View

    private lateinit var viewModel: ProfileViewModel

    private lateinit var callbackManager: CallbackManager
    private var isProfileOwner = false
    private var profileUid: String? = null

    companion object {
        private const val SUPPORT_ACTIONBAR_SIGNED_IN = ""
        private const val SUPPORT_ACTIONBAR_NOT_SIGNED_IN = "Not Signed In"
        private const val STORAGE_IMAGES_PATH = "image/*"
        const val USER_KEY = "USER_KEY"
        var authenticationService: () -> AuthenticationService =
            { Auth.getCorrectAuthenticationService() }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Auth.setAuthenticationService(authenticationService())

        Auth.isLoggedIn.observe(this) {
            verifyAndUpdateUserIsLoggedIn()
        }

        profileUid = intent.getStringExtra(USER_KEY)
        if (profileUid == null) {
            if (Auth.isLoggedIn.value!!) {
                profileUid = Auth.getCurrentUser()!!.uid // By default profile we are seeing is ours
            }
        }

        initializeViews()
    }

    private fun initializeViews() {
        photoView = findViewById(R.id.photo)
        photoEditView = findViewById(R.id.photo_edit)
        nameView = findViewById(R.id.profile_name)
        bioView = findViewById(R.id.profile_bio)
        nameEditView = findViewById(R.id.profile_edit_name)
        bioEditView = findViewById(R.id.profile_edit_bio)

        initializeButtons()

        signedInView = findViewById(R.id.signed_in)

        registerFacebookCallBack()

        profileView = findViewById(R.id.profile_container)
        profileEditView = findViewById(R.id.profile_edit_container)
    }

    private fun initializeButtons() {
        profileEditButton = findViewById(R.id.profile_edit_button)
        saveButton = findViewById(R.id.save)
        cancelButton = findViewById(R.id.cancel)
        seeFriendsButton = findViewById(R.id.list_friends_button)
        signInButton = findViewById(R.id.sign_in)
        facebookSignInButton = findViewById(R.id.facebook_sign_in)
        signOutButton = findViewById(R.id.sign_out)
        postsButton = findViewById(R.id.profile_posts_button)
        favoritePoisButton = findViewById(R.id.profile_favorite_pois_button)
        lightDarkModeButton = findViewById(R.id.switch_mode)
    }

    private fun registerFacebookCallBack() {
        callbackManager = CallbackManager.Factory.create()

        facebookSignInButton.registerCallback(
            callbackManager, facebookCallback
        )
    }

    private fun setupViewModel() {
        viewModel = ViewModelProvider(this, ProfileViewModelFactory(profileUid!!))
            .get(ProfileViewModel::class.java)
        viewModel.getUser().removeObservers(this)
        viewModel.getUser().observe(this) { user ->
            nameView.text = user.username
            bioView.text = user.bio

            if (nameView.text.isEmpty()) {

                nameView.text = Auth.getCurrentUser()!!.username
            }
        }
        viewModel.getRequestCreator().removeObservers(this)
        viewModel.getRequestCreator().observe(this) { it.into(photoView) }
    }

    /** Buttons callback function */
    fun onProfileButtonClick(view: View) {
        when (view) {
            photoEditView -> launchGallery.launch(STORAGE_IMAGES_PATH)
            saveButton -> saveProfile()
            cancelButton -> showProfile()

            seeFriendsButton -> showProfileOwnersInfo(FriendsListActivity::class.java)
            signInButton -> Auth.signInIntent(this)
            signOutButton -> {
                Auth.signOut()
                profileUid = null
                verifyAndUpdateUserIsLoggedIn()
            }
            profileEditButton -> showEditMode()
            postsButton -> showProfileOwnersInfo(MyPostsActivity::class.java)
            favoritePoisButton -> showProfileOwnersInfo(FavoritePoisActivity::class.java)
            lightDarkModeButton -> changeMode()
        }
    }

    private fun saveProfile() {
        val user = Auth.getCurrentUser()!!
        user.username = nameEditView.text.toString()
        user.bio = bioEditView.text.toString()
        viewModel.updateProfile(user)

        showProfile()
    }

    private fun showProfile() {
        UIUtility.hideSoftKeyboard(this)

        profileView.visibility = View.VISIBLE
        profileEditView.visibility = View.GONE
        photoEditView.visibility = View.GONE

        seeFriendsButton.isVisible = isProfileOwner
        signOutButton.isVisible = isProfileOwner
        profileEditButton.isVisible = isProfileOwner
    }

    private fun showEditMode() {
        nameEditView.setText(nameView.text)
        bioEditView.setText(bioView.text)

        profileView.visibility = View.GONE
        seeFriendsButton.visibility = View.GONE
        signOutButton.visibility = View.GONE
        profileEditView.visibility = View.VISIBLE
        photoEditView.visibility = View.VISIBLE
        profileEditButton.visibility = View.GONE
        photoEditView.visibility = View.VISIBLE
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        Auth.onActivityResult(this, requestCode, resultCode, data) {}
        callbackManager.onActivityResult(requestCode, resultCode, data)
    }

    private fun verifyAndUpdateUserIsLoggedIn() {
        if (Auth.isLoggedIn.value!! || profileUid != null) {
            supportActionBar?.title = SUPPORT_ACTIONBAR_SIGNED_IN
            signedInView.visibility = View.VISIBLE
            signInButton.visibility = View.GONE
            facebookSignInButton.visibility = View.GONE

            if (profileUid == null) {
                profileUid = Auth.getCurrentUser()!!.uid
            }

            setupViewModel()
            updateIsProfileOwner()
            showProfile()
        } else {
            supportActionBar?.title = SUPPORT_ACTIONBAR_NOT_SIGNED_IN
            signedInView.visibility = View.GONE
            signInButton.visibility = View.VISIBLE
            facebookSignInButton.visibility = View.VISIBLE
            signOutButton.visibility = View.GONE
        }
    }


    private fun updateIsProfileOwner() {
        if (Auth.isLoggedIn.value!!) {
            val authUser = Auth.getCurrentUser()!!
            isProfileOwner = (authUser.uid == profileUid)
        } else {
            isProfileOwner = false
        }
    }

    private fun showProfileOwnersInfo(activityClass: Class<out AppCompatActivity>) {
        val intent = Intent(this, activityClass)
            .putExtra(USER_KEY, profileUid)
        startActivity(intent)
    }

    private fun changeMode() {
        val builder = AlertDialog.Builder(this)
        builder.setTitle("Choose Mode")
        val styles = arrayOf("System default", "Light", "Dark")

        val preferences = UserPreferences(this)

        builder.setSingleChoiceItems(styles, preferences.darkMode) { dialog, which ->
            preferences.applyMode(which)
            preferences.darkMode = which
            dialog.dismiss()
        }

        val dialog = builder.create()
        dialog.show()
    }

    private val facebookCallback = object : FacebookCallback<LoginResult> {
        private lateinit var profileTracker: ProfileTracker

        override fun onSuccess(loginResult: LoginResult?) {
            if (Profile.getCurrentProfile() == null) {
                profileTracker = object : ProfileTracker() {
                    override fun onCurrentProfileChanged(
                        oldProfile: Profile?,
                        currentProfile: Profile
                    ) {
                        Auth.setAuthenticationService(FacebookAuthenticationService())
                        profileTracker.stopTracking()
                        viewModel.updateProfile(Auth.getCurrentUser()!!)
                    }
                }
            } else {
                Auth.setAuthenticationService(FacebookAuthenticationService())
            }
        }

        override fun onCancel() {}

        override fun onError(exception: FacebookException) {}
    }
}