Co-authored-by: Prat T <pt2121@users.noreply.github.com>
This commit is contained in:
parent
c8fc2418b8
commit
5bc680ab78
2 changed files with 127 additions and 77 deletions
app/src/main/java/com/keylesspalace/tusky/components/compose
|
@ -68,7 +68,7 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.adapter.EmojiAdapter
|
||||
import com.keylesspalace.tusky.adapter.LocaleAdapter
|
||||
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
||||
import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.CaptionDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
|
||||
import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener
|
||||
import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView
|
||||
|
@ -118,7 +118,8 @@ class ComposeActivity :
|
|||
OnEmojiSelectedListener,
|
||||
Injectable,
|
||||
OnReceiveContentListener,
|
||||
ComposeScheduleView.OnTimeSetListener {
|
||||
ComposeScheduleView.OnTimeSetListener,
|
||||
CaptionDialog.Listener {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -213,9 +214,8 @@ class ComposeActivity :
|
|||
val mediaAdapter = MediaPreviewAdapter(
|
||||
this,
|
||||
onAddCaption = { item ->
|
||||
makeCaptionDialog(item.description, item.uri) { newDescription ->
|
||||
viewModel.updateDescription(item.localId, newDescription)
|
||||
}
|
||||
CaptionDialog.newInstance(item.localId, item.description, item.uri)
|
||||
.show(supportFragmentManager, "caption_dialog")
|
||||
},
|
||||
onEditImage = this::editImageInQueue,
|
||||
onRemove = this::removeMediaFromQueue
|
||||
|
@ -1149,6 +1149,14 @@ class ComposeActivity :
|
|||
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
|
||||
override fun onUpdateDescription(localId: Int, description: String) {
|
||||
lifecycleScope.launch {
|
||||
if (!viewModel.updateDescription(localId, description)) {
|
||||
Toast.makeText(this@ComposeActivity, R.string.error_failed_set_caption, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class ComposeOptions(
|
||||
// Let's keep fields var until all consumers are Kotlin
|
||||
|
|
|
@ -15,19 +15,22 @@
|
|||
|
||||
package com.keylesspalace.tusky.components.compose.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.InputFilter
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
|
||||
|
@ -35,85 +38,124 @@ import com.bumptech.glide.request.target.CustomTarget
|
|||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import com.keylesspalace.tusky.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// https://github.com/tootsuite/mastodon/blob/c6904c0d3766a2ea8a81ab025c127169ecb51373/app/models/media_attachment.rb#L32
|
||||
private const val MEDIA_DESCRIPTION_CHARACTER_LIMIT = 1500
|
||||
|
||||
fun <T> T.makeCaptionDialog(
|
||||
existingDescription: String?,
|
||||
previewUri: Uri,
|
||||
onUpdateDescription: suspend (String) -> Boolean
|
||||
) where T : Activity, T : LifecycleOwner {
|
||||
val dialogLayout = LinearLayout(this)
|
||||
val padding = Utils.dpToPx(this, 8)
|
||||
dialogLayout.setPadding(padding, padding, padding, padding)
|
||||
class CaptionDialog : DialogFragment() {
|
||||
|
||||
dialogLayout.orientation = LinearLayout.VERTICAL
|
||||
val imageView = PhotoView(this).apply {
|
||||
maximumScale = 6f
|
||||
}
|
||||
private lateinit var listener: Listener
|
||||
private lateinit var input: EditText
|
||||
|
||||
val margin = Utils.dpToPx(this, 4)
|
||||
dialogLayout.addView(imageView)
|
||||
(imageView.layoutParams as LinearLayout.LayoutParams).weight = 1f
|
||||
imageView.layoutParams.height = 0
|
||||
(imageView.layoutParams as LinearLayout.LayoutParams).setMargins(0, margin, 0, 0)
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val context = requireContext()
|
||||
val dialogLayout = LinearLayout(context)
|
||||
val padding = Utils.dpToPx(context, 8)
|
||||
dialogLayout.setPadding(padding, padding, padding, padding)
|
||||
|
||||
val input = EditText(this)
|
||||
input.hint = resources.getQuantityString(
|
||||
R.plurals.hint_describe_for_visually_impaired,
|
||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT, MEDIA_DESCRIPTION_CHARACTER_LIMIT
|
||||
)
|
||||
dialogLayout.addView(input)
|
||||
(input.layoutParams as LinearLayout.LayoutParams).setMargins(margin, margin, margin, margin)
|
||||
input.setLines(2)
|
||||
input.inputType = (
|
||||
InputType.TYPE_CLASS_TEXT
|
||||
or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||
)
|
||||
input.setText(existingDescription)
|
||||
input.filters = arrayOf(InputFilter.LengthFilter(MEDIA_DESCRIPTION_CHARACTER_LIMIT))
|
||||
|
||||
val okListener = { dialog: DialogInterface, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
if (!onUpdateDescription(input.text.toString())) {
|
||||
showFailedCaptionMessage()
|
||||
}
|
||||
dialogLayout.orientation = LinearLayout.VERTICAL
|
||||
val imageView = PhotoView(context).apply {
|
||||
maximumScale = 6f
|
||||
}
|
||||
dialog.dismiss()
|
||||
|
||||
val margin = Utils.dpToPx(context, 4)
|
||||
dialogLayout.addView(imageView)
|
||||
(imageView.layoutParams as LinearLayout.LayoutParams).weight = 1f
|
||||
imageView.layoutParams.height = 0
|
||||
(imageView.layoutParams as LinearLayout.LayoutParams).setMargins(0, margin, 0, 0)
|
||||
|
||||
input = EditText(context)
|
||||
input.hint = resources.getQuantityString(
|
||||
R.plurals.hint_describe_for_visually_impaired,
|
||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT, MEDIA_DESCRIPTION_CHARACTER_LIMIT
|
||||
)
|
||||
dialogLayout.addView(input)
|
||||
(input.layoutParams as LinearLayout.LayoutParams).setMargins(margin, margin, margin, margin)
|
||||
input.setLines(2)
|
||||
input.inputType = (
|
||||
InputType.TYPE_CLASS_TEXT
|
||||
or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||
)
|
||||
input.filters = arrayOf(InputFilter.LengthFilter(MEDIA_DESCRIPTION_CHARACTER_LIMIT))
|
||||
input.setText(arguments?.getString(EXISTING_DESCRIPTION_ARG))
|
||||
|
||||
val localId = arguments?.getInt(LOCAL_ID_ARG) ?: error("Missing localId")
|
||||
val dialog = AlertDialog.Builder(context)
|
||||
.setView(dialogLayout)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
listener.onUpdateDescription(localId, input.text.toString())
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
|
||||
isCancelable = false
|
||||
val window = dialog.window
|
||||
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
|
||||
val previewUri =
|
||||
arguments?.getParcelable<Uri>(PREVIEW_URI_ARG) ?: error("Preview Uri is null")
|
||||
// Load the image and manually set it into the ImageView because it doesn't have a fixed size.
|
||||
Glide.with(this)
|
||||
.load(previewUri)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.into(object : CustomTarget<Drawable>(4096, 4096) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?,
|
||||
) {
|
||||
imageView.setImageDrawable(resource)
|
||||
}
|
||||
})
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
.setView(dialogLayout)
|
||||
.setPositiveButton(android.R.string.ok, okListener)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putString(DESCRIPTION_KEY, input.text.toString())
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
val window = dialog.window
|
||||
window?.setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
||||
)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
savedInstanceState?.getString(DESCRIPTION_KEY)?.let {
|
||||
input.setText(it)
|
||||
}
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
listener = context as? Listener ?: error("Activity is not ComposeCaptionDialog.Listener")
|
||||
}
|
||||
|
||||
// Load the image and manually set it into the ImageView because it doesn't have a fixed size.
|
||||
Glide.with(this)
|
||||
.load(previewUri)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.into(object : CustomTarget<Drawable>(4096, 4096) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
interface Listener {
|
||||
fun onUpdateDescription(localId: Int, description: String)
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
|
||||
imageView.setImageDrawable(resource)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun Activity.showFailedCaptionMessage() {
|
||||
Toast.makeText(this, R.string.error_failed_set_caption, Toast.LENGTH_SHORT).show()
|
||||
companion object {
|
||||
fun newInstance(
|
||||
localId: Int,
|
||||
existingDescription: String?,
|
||||
previewUri: Uri,
|
||||
) = CaptionDialog().apply {
|
||||
arguments = bundleOf(
|
||||
LOCAL_ID_ARG to localId,
|
||||
EXISTING_DESCRIPTION_ARG to existingDescription,
|
||||
PREVIEW_URI_ARG to previewUri,
|
||||
)
|
||||
}
|
||||
|
||||
private const val DESCRIPTION_KEY = "description"
|
||||
private const val EXISTING_DESCRIPTION_ARG = "existing_description"
|
||||
private const val PREVIEW_URI_ARG = "preview_uri"
|
||||
private const val LOCAL_ID_ARG = "local_id"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue