asfoury/projmag

View on GitHub
app/src/main/java/com/sdp13epfl2021/projmag/activities/ProjectCreationActivity.kt

Summary

Maintainability
A
1 hr
Test Coverage
D
62%
package com.sdp13epfl2021.projmag.activities

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.view.View
import android.view.View.VISIBLE
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.sdp13epfl2021.projmag.MainActivity
import com.sdp13epfl2021.projmag.R
import com.sdp13epfl2021.projmag.database.ProjectUploader
import com.sdp13epfl2021.projmag.database.interfaces.CandidatureDatabase
import com.sdp13epfl2021.projmag.database.interfaces.FileDatabase
import com.sdp13epfl2021.projmag.database.interfaces.MetadataDatabase
import com.sdp13epfl2021.projmag.database.interfaces.ProjectDatabase
import com.sdp13epfl2021.projmag.model.ImmutableProject
import com.sdp13epfl2021.projmag.model.Result
import com.sdp13epfl2021.projmag.video.VideoUtils
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import javax.inject.Named


const val FORM_TO_SUBTITLE_MESSAGE = "com.sdp13epfl2021.projmag.activities.FORM_TO_SUBTITLE_MESSAGE"

/**
 * Activity consisting of a form one can use to create and submit a project.
 */
@AndroidEntryPoint
class ProjectCreationActivity : AppCompatActivity() {

    companion object {
        private const val REQUEST_VIDEO_ACCESS = 1
        private const val REQUEST_VIDEO_SUBTITLING = 2
        private const val REQUEST_TAG_ACCESS = 3
        private const val REQUEST_SELECTION_ACCESS = 4
        const val EDIT_EXTRA = "edit"
    }

    private var projectToEdit: ImmutableProject? = null
    private var changedVid = false

    @Inject
    lateinit var projectDB: ProjectDatabase

    @Inject
    lateinit var fileDB: FileDatabase

    @Inject
    lateinit var metadataDB: MetadataDatabase

    @Inject
    lateinit var candidatureDB: CandidatureDatabase

    @Inject
    @Named("currentUserId")
    lateinit var userID: String

    //tag selection related variables
    private lateinit var tagRecyclerView: RecyclerView

    private var videoUri: Uri? = null
    private var subtitles: String? = null
    private var listTags: Array<String> = emptyArray()
    private var listSections: Array<String> = emptyArray()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_project_creation)
        projectToEdit = intent.getParcelableExtra(EDIT_EXTRA) as ImmutableProject?

        setUpButtons()

        projectToEdit?.let {
            setInitialValues(it)
        }
    }

    private fun setUpButtons() {
        val addVideoButton: Button = findViewById(R.id.add_video)
        val addTagButton: Button = findViewById(R.id.addTagsButton)
        val addSectionButton: Button = findViewById(R.id.addSectionButton)
        addVideoButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
            startActivityForResult(
                Intent.createChooser(intent, "Select Video"),
                REQUEST_VIDEO_ACCESS
            )
        }

        findViewById<Button>(R.id.form_button_sub)?.setOnClickListener(::submit)
        addTagButton.setOnClickListener {
            switchToTagsSelectionActivity()
        }
        addSectionButton.setOnClickListener {
            switchToSectionSelectionActivity()
        }
        findViewById<TextView>(R.id.title_form).requestFocus()
        findViewById<Button>(R.id.form_add_subtitle).setOnClickListener(::onClickSubtitleButton)
        findViewById<Button>(R.id.form_button_sub).setOnClickListener(::submit)
    }

    private fun setInitialValues(project: ImmutableProject) {
        findViewById<TextView>(R.id.form_edit_text_project_name).text = project.name
        findViewById<TextView>(R.id.form_edit_text_laboratory).text = project.lab
        findViewById<TextView>(R.id.form_edit_text_teacher).text = project.teacher
        findViewById<TextView>(R.id.form_edit_text_project_TA).text = project.TA
        findViewById<TextView>(R.id.form_nb_of_participant).text = project.nbParticipant.toString()
        findViewById<CheckBox>(R.id.form_check_box_SP).isChecked = project.bachelorProject
        findViewById<CheckBox>(R.id.form_check_box_MP).isChecked = project.masterProject
        findViewById<TextView>(R.id.form_project_description).text = project.description
        listTags = project.tags.toTypedArray()
        listSections = project.allowedSections.toTypedArray()
        if (!project.videoUri.isEmpty()) {
            videoUri = Uri.parse(project.videoUri[0])
            setUpVideo()
        }
    }

    /**
     * Disable submission button
     * Useful to submit only one project at time
     */
    private fun setSubmitButtonEnabled(enabled: Boolean) = runOnUiThread {
        findViewById<Button>(R.id.form_button_sub).apply {
            isEnabled = enabled
            text = if (enabled) getString(R.string.submission) else getString(R.string.loading)
        }
    }

    private fun onClickSubtitleButton(view: View) {
        val intent = Intent(this, VideoSubtitlingActivity::class.java).apply {
            putExtra(FORM_TO_SUBTITLE_MESSAGE, videoUri.toString())
        }
        startActivityForResult(
            intent,
            REQUEST_VIDEO_SUBTITLING
        )
    }

    private fun setUpVideo() {
        val vidView = findViewById<VideoView>(R.id.videoView)
        val playVidButton = findViewById<Button>(R.id.play_video)
        val subtitleButton = findViewById<Button>(R.id.form_add_subtitle)
        val mediaController = MediaController(this)

        FormHelper.playVideoFromLocalPath(
            playVidButton,
            subtitleButton,
            vidView,
            mediaController,
            videoUri!!
        )
    }

    /**
     * This function is called after the user comes back
     * from selecting a video from the file explorer
     */
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        val vidView = findViewById<VideoView>(R.id.videoView)
        changedVid = true
        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_VIDEO_ACCESS) {
            if (data?.data != null) {
                // THIS IS THE VID URI
                videoUri = data.data

                setUpVideo()
            }
        } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_VIDEO_SUBTITLING) {
            if (data != null) {
                data.getStringExtra(VIDEO_SUBTITLING_ACTIVITY_RESULT_KEY)?.let {
                    subtitles = it
                    vidView.addSubtitleSource(
                        it.byteInputStream(),
                        VideoUtils.ENGLISH_WEBVTT_SUBTITLE_FORMAT
                    )
                }
            }
        } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_TAG_ACCESS) {
            if (data != null) {
                val tagData = data.getStringArrayExtra(MainActivity.tagsList)
                if (tagData != null) {
                    listTags = tagData
                }
            }
        } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_SELECTION_ACCESS) {
            if (data != null) {
                val secList = data.getStringArrayExtra(MainActivity.sectionsList)
                if (secList != null) {
                    listSections = secList
                }
            }
        }
    }


    /**
     * Extract string text content form an EditText view
     */
    private fun getTextFromEditText(id: Int): String = findViewById<EditText>(id).run {
        text.toString()
    }

    /**
     * Show a toast message on the UI thread
     * This is useful when using async callbacks
     */
    private fun showToast(msg: String) = runOnUiThread {
        Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
    }

    /**
     * Construct a Project with data present in the view
     */
    private fun constructProject(): Result<ImmutableProject> {
        return ImmutableProject.build(
            id = if (projectToEdit == null) "" else projectToEdit!!.id, //id is defined by firebase itself
            name = getTextFromEditText(R.id.form_edit_text_project_name),
            lab = getTextFromEditText(R.id.form_edit_text_laboratory),
            authorId = userID,
            teacher = getTextFromEditText(R.id.form_edit_text_teacher),
            TA = getTextFromEditText(R.id.form_edit_text_project_TA),
            nbParticipant = try {
                getTextFromEditText(R.id.form_nb_of_participant).toInt()
            } catch (_: NumberFormatException) {
                0
            },
            masterProject = findViewById<CheckBox>(R.id.form_check_box_MP).isChecked,
            bachelorProject = findViewById<CheckBox>(R.id.form_check_box_SP).isChecked,
            isTaken = false,
            description = getTextFromEditText(R.id.form_project_description),
            assigned = listOf(),
            tags = listTags.toList(),
            allowedSections = listSections.toList(),
            videoURI = if (videoUri != null) listOf(videoUri.toString()) else listOf()
        )
    }


    /**
     * Finish the activity from another thread
     * Useful when using async callbacks
     */
    private fun finishFromOtherThread() = runOnUiThread {
        finish()
    }

    /**
     * Submit project and video with information in the view.
     * Expected to be called when clicking on a submission button on the view
     */
    private fun submit(view: View) {
        setSubmitButtonEnabled(false) // disable submit, as there is a long time uploading video
        ProjectUploader(
            projectDB,
            fileDB,
            metadataDB,
            candidatureDB,
            ::showToast,
            { setSubmitButtonEnabled(true) },
            ::finishFromOtherThread
        ).checkProjectAndThenUpload(
            constructProject(),
            if (changedVid) videoUri else null,
            subtitles
        )
    }

    /**
     * Switch to the tag selection activity that will then comeback to this activity
     * And update the project list if the activity finishes properly
     */
    private fun switchToTagsSelectionActivity() {
        //why do i need to do the :: class.java to make it work
        val intent = Intent(this, TagsSelectorActivity::class.java)
        startActivityForResult(intent, REQUEST_TAG_ACCESS)

    }

    private fun switchToSectionSelectionActivity() {
        val intent = Intent(this, SectionSelectionActivity::class.java)
        startActivityForResult(intent, REQUEST_SELECTION_ACCESS)
    }


}

object FormHelper {
    fun playVideoFromLocalPath(
        playVidButton: Button,
        subtitleButton: Button,
        vidView: VideoView,
        mediaController: MediaController,
        uri: Uri
    ) {
        playVidButton.isEnabled = true
        playVidButton.setOnClickListener {
            vidView.setMediaController(mediaController)
            vidView.setVideoURI(uri)
            vidView.start()
            vidView.visibility = VISIBLE
        }
        subtitleButton.isEnabled = true
    }
}