package app.gedama.tutorial.cruddiary.fragment
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.speech.RecognizerIntent
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TableRow
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isInvisible
import app.gedama.tutorial.cruddiary.R
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.lifecycleScope
import app.gedama.tutorial.cruddiary.GlobalAppStore
import app.gedama.tutorial.cruddiary.data.Spinnable
import app.gedama.tutorial.cruddiary.ui.formfields.FormFieldText
import app.gedama.tutorial.cruddiary.ui.formfields.validate
import app.gedama.tutorial.cruddiary.viewmodel.BaseViewModel
import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.textfield.TextInputEditText
import kotlinx.coroutines.launch
import java.io.File
import com.github.dhaval2404.imagepicker.ImagePicker
import com.google.android.material.textfield.TextInputLayout
abstract class BaseFragment: Fragment() {
val TAG = this::class.simpleName
fun saveToUpdateButton(saveButton: Button) {
saveButton.text = resources.getString(R.string.update_button)
}
fun setDeactivateButton(deactivateButton: MaterialButton, viewModelFunc: () -> Unit ) {
deactivateButton.setOnClickListener {
val builder = AlertDialog.Builder(this.requireContext())
builder.setTitle(resources.getText(R.string.inactivity_safety_dialog_title))
builder.setMessage(resources.getText(R.string.inactivity_safety_check))
builder.setPositiveButton(R.string.yes) { _, _ ->
viewModelFunc()
}
builder.setNegativeButton(R.string.no) { _, _ ->
}
builder.show()
}
}
fun setInactivityButton(inactive: Boolean, deactivateButton: MaterialButton, checkBoxInactive: CheckBox) {
if ( inactive ) {
deactivateButton.visibility = View.GONE
checkBoxInactive.isVisible = true
}
}
fun activateAddFAB(viewModelFunc: () -> Unit ) {
val fab = this.requireActivity().findViewById<FloatingActionButton>(R.id.add_floating_action_button)
fab.isVisible = true
fab.setOnClickListener { viewModelFunc() }
}
fun deactivateAddFAB() {
val fab = this.requireActivity().findViewById<FloatingActionButton>(R.id.add_floating_action_button)
fab.isVisible = false
}
fun formValidation(actionButton: Button, formFields: List<FormFieldText>, viewModel: BaseViewModel) = lifecycleScope.launch {
actionButton.isEnabled = false
if (formFields.validate(validateAll = true)) {
viewModel.updateEntryAfterValidation()
viewModel.onValidatedAndUpdated()
} else {
viewModel._deactivateSuccess.value = false
}
actionButton.isEnabled = true
}
fun initMaterialSpinnerData(spinner: Spinner, textEdit: TextInputEditText, listOfItems: LiveData<List<Spinnable>>, viewModelFunc: (item: Spinnable) -> Unit ) {
textEdit.setOnClickListener {
spinner.performClick()
}
val allItems = context?.let {
ArrayAdapter<Any>(it, android.R.layout.simple_dropdown_item_1line)
}
listOfItems
.observe(viewLifecycleOwner, { items ->
items?.forEach {
allItems?.add(it)
}
})
spinner.adapter = allItems
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedItem = parent!!.adapter.getItem(position) as Spinnable
textEdit.setText(selectedItem.itemName())
viewModelFunc(selectedItem)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
fun initImagePicking(iv: ImageView, ib: Button, viewModel: BaseViewModel, standardImageButton: Button? = null,
maxSize: Int = 1024, maxWidth: Int = 1080, maxHeight: Int = 1080, cropX: Float = 1F, cropY: Float = 1F) {
val startForProfileImageResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == Activity.RESULT_OK) {
val fileUri = data?.data!!
iv.setImageURI(fileUri)
Log.d(TAG,"URI of selected image: $fileUri")
val bitmap = iv.drawable.toBitmap()
viewModel.imageBitmap = bitmap
viewModel.defaultImage = false
ib.text = resources.getText(R.string.change_image_button)
var parentView = iv.parent
if ( parentView is TableRow) {
parentView = iv.parent as TableRow
parentView.isInvisible = false
}
if ( parentView is LinearLayout) {
parentView = iv.parent as LinearLayout
parentView.isInvisible = false
}
standardImageButton?.let {
it.isInvisible = false
}
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Log.e(TAG, ImagePicker.getError(data))
Toast.makeText(this.requireContext(), ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this.requireContext(), resources.getText(R.string.image_picking_canceled), Toast.LENGTH_SHORT).show()
}
}
ib.setOnClickListener {
ImagePicker.with(this)
.compress(maxSize)
.maxResultSize(maxWidth, maxHeight)
.crop(cropX,cropY)
.createIntent { intent ->
startForProfileImageResult.launch(intent)
}
}
}
private fun loadImage(imagePath: String): Bitmap? {
var bm: Bitmap? = null
if (imagePath.isNotBlank()) {
bm = getBitmap(imagePath)
}
return bm
}
private fun getBitmap(imagePath: String): Bitmap? {
val file = File(GlobalAppStore.appCtx!!.filesDir, imagePath)
return BitmapFactory.decodeFile(file.absolutePath)
}
fun setDeleteImageButton(deleteImageButton: MaterialButton, iv: ImageView, ivC: LinearLayout, buttonLoadPicture: MaterialButton, viewModel: BaseViewModel) {
deleteImageButton.setOnClickListener {
viewModel.imageBitmap = null
iv.setImageBitmap(null)
ivC.visibility = View.GONE
buttonLoadPicture.text = resources.getText(R.string.add_image_button)
buttonLoadPicture.setIconResource(R.drawable.addImageButtonIcon)
deleteImageButton.visibility = View.GONE
viewModel.defaultImage = true
}
}
protected fun initPDFFromStorage(pdfB: MaterialButton, pdfDeleteB: MaterialButton, pvC: LinearLayout, viewModel: BaseViewModel) {
val selectPDFFromStorageResult = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
uri?.let {
Log.d(TAG,"URI of selected PDF: $uri")
viewModel.setPdfUri(uri)
viewModel.pdfFileName = viewModel.getFilenameFromUri(uri)
pvC.isVisible = true
pdfB.text = getString(R.string.change_pdf_button)
pdfDeleteB.isVisible = true
setDeletePdfButton(pdfDeleteB,pvC,pdfB,viewModel)
}
}
pdfB.setOnClickListener {
selectPDFFromStorage(selectPDFFromStorageResult)
}
}
private fun selectPDFFromStorage(selectPDFFromStorageResult: ActivityResultLauncher<String>) = selectPDFFromStorageResult.launch("application/pdf")
private fun setDeletePdfButton(deletePdfButton: MaterialButton, pvC: LinearLayout, buttonAddPdf: MaterialButton, viewModel: BaseViewModel) {
deletePdfButton.setOnClickListener {
viewModel.setPdfUri(null)
pvC.visibility = View.GONE
buttonAddPdf.text = resources.getText(R.string.add_pdf_button)
deletePdfButton.visibility = View.GONE
}
}
fun setChangeImageButton(view: View, imagePath: String, viewModel: BaseViewModel) {
val iv = view.findViewById<ImageView>(R.id.imageView)
val ivC = view.findViewById<LinearLayout>(R.id.image_view_container)
val buttonLoadPicture = view.findViewById<MaterialButton>(R.id.buttonLoadPicture)
val buttonDefaultPicture = view.findViewById<Button>(R.id.buttonDefaultPicture)
viewModel.setOldImage( imagePath )
val bm = loadImage( imagePath )
if ( bm != null ) {
iv.setImageBitmap(bm)
ivC.isInvisible = false
buttonLoadPicture.text = resources.getText(R.string.change_image_button)
buttonLoadPicture.setIconResource(R.drawable.changeImageButtonIcon)
buttonDefaultPicture.isInvisible = false
viewModel.defaultImage = false
}
}
fun setChangePdfButton(view: View, pdfPath: String, viewModel: BaseViewModel, fileName: String) {
val pvC = view.findViewById<LinearLayout>(R.id.pdf_view_container)
val buttonAddPdf = view.findViewById<MaterialButton>(R.id.add_pdf_button)
val buttonDeletePdf = view.findViewById<MaterialButton>(R.id.delete_pdf_button)
viewModel.setOldPdf( pdfPath )
if ( pdfPath.isNotBlank() ) {
pvC.isInvisible = false
buttonAddPdf.text = resources.getText(R.string.change_pdf_button)
buttonDeletePdf.isInvisible = false
setDeletePdfButton(buttonDeletePdf,pvC,buttonAddPdf,viewModel)
val file = File(GlobalAppStore.appCtx!!.filesDir, pdfPath)
val fileUri = FileProvider.getUriForFile(GlobalAppStore.appCtx!!,"app.gedama.tutorial.cruddiary.provider",file)
if ( fileUri != null ) {
viewModel.pdfFileName = fileName
viewModel.setPdfUri(fileUri)
}
}
}
fun doSpeechRecognition(layoutEndIcon: TextInputLayout, textField: TextInputEditText ) {
val startForSpeechRecognitionResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == Activity.RESULT_OK) {
val spokenText: String? =
data!!.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).let { results ->
results!![0]
}
spokenText?.let {
var newText: String = textField.text.toString()
if ( newText.isBlank() ) {
newText = it
} else {
newText += " " + it
}
Log.d(TAG,"Recognized text: $it")
textField.setText(newText)
}
} else {
Log.d(TAG,"Speech recogintion was not successful: $resultCode")
}
}
layoutEndIcon.setEndIconOnClickListener {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
putExtra(
RecognizerIntent.EXTRA_ENABLE_FORMATTING,
RecognizerIntent.FORMATTING_OPTIMIZE_QUALITY
)
}
}
startForSpeechRecognitionResult.launch(intent)
}
}
}