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