kopia lustrzana https://github.com/ryukoposting/Signal-Android
Story Viewer shared element transition.
rodzic
6a5f5f4ffa
commit
6820b84921
|
@ -403,7 +403,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".stories.viewer.StoryViewerActivity"
|
android:name=".stories.viewer.StoryViewerActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:theme="@style/TextSecure.DarkNoActionBar"
|
android:theme="@style/TextSecure.DarkNoActionBar.StoryViewer"
|
||||||
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing" />
|
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing" />
|
||||||
|
|
||||||
<activity android:name=".components.settings.app.changenumber.ChangeNumberLockActivity"
|
<activity android:name=".components.settings.app.changenumber.ChangeNumberLockActivity"
|
||||||
|
|
|
@ -77,6 +77,7 @@ public abstract class MediaPreviewFragment extends Fragment {
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
checkMediaStillAvailable();
|
checkMediaStillAvailable();
|
||||||
|
requireActivity().supportStartPostponedEnterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package org.thoughtcrime.securesms.stories
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
|
import android.animation.PropertyValuesHolder.ofFloat
|
||||||
|
import android.content.Context
|
||||||
|
import android.transition.Transition
|
||||||
|
import android.transition.TransitionValues
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
|
||||||
|
class ScaleTransition : Transition {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val LAYOUT_WIDTH = "ScaleTransition:layout_width"
|
||||||
|
private const val LAYOUT_HEIGHT = "ScaleTransition:layout_height"
|
||||||
|
private const val POSITION_X = "ScaleTransition:position_x"
|
||||||
|
private const val POSITION_Y = "ScaleTransition:position_y"
|
||||||
|
private const val SCALE_X = "ScaleTransition:scale_x"
|
||||||
|
private const val SCALE_Y = "ScaleTransition:scale_y"
|
||||||
|
|
||||||
|
private val PROPERTIES = arrayOf(
|
||||||
|
LAYOUT_WIDTH,
|
||||||
|
LAYOUT_HEIGHT,
|
||||||
|
POSITION_X,
|
||||||
|
POSITION_Y,
|
||||||
|
SCALE_X,
|
||||||
|
SCALE_Y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() : super()
|
||||||
|
|
||||||
|
@RequiresApi(21)
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||||
|
|
||||||
|
override fun getTransitionProperties(): Array<String> {
|
||||||
|
return PROPERTIES
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||||
|
if (ViewCompat.getTransitionName(transitionValues.view) == "story") {
|
||||||
|
captureValues(transitionValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun captureEndValues(transitionValues: TransitionValues) {
|
||||||
|
if (ViewCompat.getTransitionName(transitionValues.view) == "story") {
|
||||||
|
resetValues(transitionValues.view)
|
||||||
|
captureValues(transitionValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun captureValues(transitionValues: TransitionValues) = with(transitionValues.view) {
|
||||||
|
transitionValues.values[LAYOUT_WIDTH] = width.toFloat()
|
||||||
|
transitionValues.values[LAYOUT_HEIGHT] = height.toFloat()
|
||||||
|
transitionValues.values[POSITION_X] = x
|
||||||
|
transitionValues.values[POSITION_Y] = y
|
||||||
|
transitionValues.values[SCALE_X] = scaleX
|
||||||
|
transitionValues.values[SCALE_Y] = scaleY
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetValues(view: View) = with(view) {
|
||||||
|
translationX = 0f
|
||||||
|
translationY = 0f
|
||||||
|
scaleX = 1f
|
||||||
|
scaleY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAnimator(
|
||||||
|
sceneRoot: ViewGroup,
|
||||||
|
start: TransitionValues?,
|
||||||
|
end: TransitionValues?
|
||||||
|
): Animator? {
|
||||||
|
if (start == null || end == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val startWidth = start.values[LAYOUT_WIDTH] as Float
|
||||||
|
val endWidth = end.values[LAYOUT_WIDTH] as Float
|
||||||
|
val startHeight = start.values[LAYOUT_HEIGHT] as Float
|
||||||
|
val endHeight = end.values[LAYOUT_HEIGHT] as Float
|
||||||
|
|
||||||
|
val startX = start.values[POSITION_X] as Float
|
||||||
|
val endX = end.values[POSITION_X] as Float
|
||||||
|
val startY = start.values[POSITION_Y] as Float
|
||||||
|
val endY = end.values[POSITION_Y] as Float
|
||||||
|
|
||||||
|
val startScaleX = start.values[SCALE_X] as Float
|
||||||
|
val startScaleY = start.values[SCALE_Y] as Float
|
||||||
|
|
||||||
|
end.view.translationX = (startX - endX) - (endWidth - startWidth) / 2
|
||||||
|
end.view.translationY = (startY - endY) - (endHeight - startHeight) / 2
|
||||||
|
|
||||||
|
end.view.scaleX = (startWidth / endWidth) * startScaleX
|
||||||
|
end.view.scaleY = (startHeight / endHeight) * startScaleY
|
||||||
|
|
||||||
|
return ObjectAnimator.ofPropertyValuesHolder(end.view,
|
||||||
|
ofFloat(View.TRANSLATION_X, 0f),
|
||||||
|
ofFloat(View.TRANSLATION_Y, 0f),
|
||||||
|
ofFloat(View.SCALE_X, 1f),
|
||||||
|
ofFloat(View.SCALE_Y, 1f)).apply {
|
||||||
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
|
resetValues(start.view)
|
||||||
|
resetValues(end.view)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,17 +7,15 @@ import android.graphics.drawable.Drawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.doAfterTextChanged
|
|
||||||
import com.airbnb.lottie.SimpleColorFilter
|
import com.airbnb.lottie.SimpleColorFilter
|
||||||
import com.google.android.material.imageview.ShapeableImageView
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
import org.signal.core.util.DimensionUnit
|
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
||||||
|
@ -30,7 +28,6 @@ import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryScale
|
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryScale
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryTextWatcher
|
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryTextWatcher
|
||||||
import org.thoughtcrime.securesms.stories.viewer.page.StoryDisplay
|
import org.thoughtcrime.securesms.stories.viewer.page.StoryDisplay
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
|
||||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
|
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
|
||||||
import org.thoughtcrime.securesms.util.visible
|
import org.thoughtcrime.securesms.util.visible
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -47,26 +44,13 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
|
|
||||||
private var textAlignment: TextAlignment? = null
|
private var textAlignment: TextAlignment? = null
|
||||||
private val backgroundView: ShapeableImageView = findViewById(R.id.text_story_post_background)
|
private val backgroundView: ShapeableImageView = findViewById(R.id.text_story_post_background)
|
||||||
private val textView: TextView = findViewById(R.id.text_story_post_text)
|
private val textView: StoryTextView = findViewById(R.id.text_story_post_text)
|
||||||
private val linkPreviewView: StoryLinkPreviewView = findViewById(R.id.text_story_post_link_preview)
|
private val linkPreviewView: StoryLinkPreviewView = findViewById(R.id.text_story_post_link_preview)
|
||||||
|
|
||||||
private var isPlaceholder: Boolean = true
|
private var isPlaceholder: Boolean = true
|
||||||
|
|
||||||
init {
|
init {
|
||||||
backgroundView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
|
||||||
textView.maxWidth = backgroundView.measuredWidth - DimensionUnit.DP.toPixels(40f).toInt()
|
|
||||||
|
|
||||||
textAlignment?.apply {
|
|
||||||
adjustTextTranslationX(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextStoryTextWatcher.install(textView)
|
TextStoryTextWatcher.install(textView)
|
||||||
textView.doAfterTextChanged {
|
|
||||||
textAlignment?.apply {
|
|
||||||
adjustTextTranslationX(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val displaySize = StoryDisplay.getStoryDisplay(
|
val displaySize = StoryDisplay.getStoryDisplay(
|
||||||
resources.displayMetrics.widthPixels.toFloat(),
|
resources.displayMetrics.widthPixels.toFloat(),
|
||||||
|
@ -126,13 +110,7 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTextBackgroundColor(@ColorInt color: Int) {
|
fun setTextBackgroundColor(@ColorInt color: Int) {
|
||||||
if (color == Color.TRANSPARENT) {
|
textView.setWrappedBackgroundColor(color)
|
||||||
textView.background = null
|
|
||||||
} else {
|
|
||||||
textView.background = AppCompatResources.getDrawable(context, R.drawable.rounded_rectangle_secondary_18)?.apply {
|
|
||||||
colorFilter = SimpleColorFilter(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindFromCreationState(state: TextStoryPostCreationState) {
|
fun bindFromCreationState(state: TextStoryPostCreationState) {
|
||||||
|
@ -151,7 +129,6 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
setTextGravity(state.textAlignment)
|
setTextGravity(state.textAlignment)
|
||||||
setTextScale(state.textScale)
|
setTextScale(state.textScale)
|
||||||
|
|
||||||
postAdjustTextTranslationX(state.textAlignment)
|
|
||||||
postAdjustLinkPreviewTranslationY()
|
postAdjustLinkPreviewTranslationY()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +150,11 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
|
|
||||||
hideCloseButton()
|
hideCloseButton()
|
||||||
|
|
||||||
postAdjustTextTranslationX(TextAlignment.CENTER)
|
|
||||||
postAdjustLinkPreviewTranslationY()
|
postAdjustLinkPreviewTranslationY()
|
||||||
|
|
||||||
|
doOnNextLayout {
|
||||||
|
(context as? AppCompatActivity)?.supportStartPostponedEnterTransition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindLinkPreview(linkPreview: LinkPreview?): ListenableFuture<Boolean> {
|
fun bindLinkPreview(linkPreview: LinkPreview?): ListenableFuture<Boolean> {
|
||||||
|
@ -192,12 +172,6 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postAdjustTextTranslationX(textAlignment: TextAlignment) {
|
|
||||||
doOnNextLayout {
|
|
||||||
adjustTextTranslationX(textAlignment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setTextViewClickListener(onClickListener: OnClickListener) {
|
fun setTextViewClickListener(onClickListener: OnClickListener) {
|
||||||
setOnClickListener(onClickListener)
|
setOnClickListener(onClickListener)
|
||||||
}
|
}
|
||||||
|
@ -250,34 +224,4 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
textView.translationY = desiredPoint - originPoint
|
textView.translationY = desiredPoint - originPoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun alignTextLeft() {
|
|
||||||
textView.translationX = DimensionUnit.DP.toPixels(20f)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun alignTextRight() {
|
|
||||||
textView.translationX = backgroundView.measuredWidth - textView.measuredWidth - DimensionUnit.DP.toPixels(20f)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun adjustTextTranslationX(textAlignment: TextAlignment) {
|
|
||||||
when (textAlignment) {
|
|
||||||
TextAlignment.CENTER -> {
|
|
||||||
textView.translationX = backgroundView.measuredWidth / 2f - textView.measuredWidth / 2f
|
|
||||||
}
|
|
||||||
TextAlignment.START -> {
|
|
||||||
if (ViewUtil.isLtr(textView)) {
|
|
||||||
alignTextLeft()
|
|
||||||
} else {
|
|
||||||
alignTextRight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextAlignment.END -> {
|
|
||||||
if (ViewUtil.isRtl(textView)) {
|
|
||||||
alignTextLeft()
|
|
||||||
} else {
|
|
||||||
alignTextRight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.thoughtcrime.securesms.stories
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import org.signal.core.util.DimensionUnit
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
|
||||||
|
class StoryTextView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null
|
||||||
|
) : EmojiTextView(context, attrs) {
|
||||||
|
|
||||||
|
private val textBounds: RectF = RectF()
|
||||||
|
private val wrappedBackgroundPaint = Paint().apply {
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
isAntiAlias = true
|
||||||
|
color = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (isInEditMode) {
|
||||||
|
wrappedBackgroundPaint.color = Color.RED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setWrappedBackgroundColor(@ColorInt color: Int) {
|
||||||
|
wrappedBackgroundPaint.color = color
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
if (wrappedBackgroundPaint.color != Color.TRANSPARENT) {
|
||||||
|
textBounds.set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
|
||||||
|
|
||||||
|
val maxWidth = (0 until layout.lineCount).map {
|
||||||
|
layout.getLineWidth(it)
|
||||||
|
}.maxOrNull()
|
||||||
|
|
||||||
|
if (maxWidth != null) {
|
||||||
|
textBounds.inset((width - maxWidth - paddingStart - paddingEnd) / 2f, 0f)
|
||||||
|
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
textBounds,
|
||||||
|
DimensionUnit.DP.toPixels(18f),
|
||||||
|
DimensionUnit.DP.toPixels(18f),
|
||||||
|
wrappedBackgroundPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDraw(canvas)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||||
|
@ -140,14 +142,15 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
||||||
private fun createStoryLandingItem(data: StoriesLandingItemData): StoriesLandingItem.Model {
|
private fun createStoryLandingItem(data: StoriesLandingItemData): StoriesLandingItem.Model {
|
||||||
return StoriesLandingItem.Model(
|
return StoriesLandingItem.Model(
|
||||||
data = data,
|
data = data,
|
||||||
onRowClick = {
|
onRowClick = { model, preview ->
|
||||||
if (it.data.storyRecipient.isMyStory) {
|
if (model.data.storyRecipient.isMyStory) {
|
||||||
startActivity(Intent(requireContext(), MyStoriesActivity::class.java))
|
startActivity(Intent(requireContext(), MyStoriesActivity::class.java))
|
||||||
} else if (it.data.primaryStory.messageRecord.isOutgoing && it.data.primaryStory.messageRecord.isFailed) {
|
} else if (model.data.primaryStory.messageRecord.isOutgoing && model.data.primaryStory.messageRecord.isFailed) {
|
||||||
lifecycleDisposable += viewModel.resend(it.data.primaryStory.messageRecord).subscribe()
|
lifecycleDisposable += viewModel.resend(model.data.primaryStory.messageRecord).subscribe()
|
||||||
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
startActivity(StoryViewerActivity.createIntent(requireContext(), it.data.storyRecipient.id))
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), preview, ViewCompat.getTransitionName(preview) ?: "")
|
||||||
|
startActivity(StoryViewerActivity.createIntent(requireContext(), model.data.storyRecipient.id), options.toBundle())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onForwardStory = {
|
onForwardStory = {
|
||||||
|
|
|
@ -35,7 +35,7 @@ object StoriesLandingItem {
|
||||||
|
|
||||||
class Model(
|
class Model(
|
||||||
val data: StoriesLandingItemData,
|
val data: StoriesLandingItemData,
|
||||||
val onRowClick: (Model) -> Unit,
|
val onRowClick: (Model, View) -> Unit,
|
||||||
val onHideStory: (Model) -> Unit,
|
val onHideStory: (Model) -> Unit,
|
||||||
val onForwardStory: (Model) -> Unit,
|
val onForwardStory: (Model) -> Unit,
|
||||||
val onShareStory: (Model) -> Unit,
|
val onShareStory: (Model) -> Unit,
|
||||||
|
@ -166,7 +166,7 @@ object StoriesLandingItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpClickListeners(model: Model) {
|
private fun setUpClickListeners(model: Model) {
|
||||||
itemView.setOnClickListener { model.onRowClick(model) }
|
itemView.setOnClickListener { model.onRowClick(model, storyPreview) }
|
||||||
|
|
||||||
if (model.data.storyRecipient.isMyStory) {
|
if (model.data.storyRecipient.isMyStory) {
|
||||||
itemView.setOnLongClickListener(null)
|
itemView.setOnLongClickListener(null)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package org.thoughtcrime.securesms.stories.my
|
package org.thoughtcrime.securesms.stories.my
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||||
|
@ -31,6 +34,12 @@ class MyStoriesFragment : DSLSettingsFragment(
|
||||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||||
MyStoriesItem.register(adapter)
|
MyStoriesItem.register(adapter)
|
||||||
|
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
||||||
viewModel.state.observe(viewLifecycleOwner) {
|
viewModel.state.observe(viewLifecycleOwner) {
|
||||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||||
|
@ -56,7 +65,7 @@ class MyStoriesFragment : DSLSettingsFragment(
|
||||||
customPref(
|
customPref(
|
||||||
MyStoriesItem.Model(
|
MyStoriesItem.Model(
|
||||||
distributionStory = conversationMessage,
|
distributionStory = conversationMessage,
|
||||||
onClick = {
|
onClick = { it, preview ->
|
||||||
if (it.distributionStory.messageRecord.isOutgoing && it.distributionStory.messageRecord.isFailed) {
|
if (it.distributionStory.messageRecord.isOutgoing && it.distributionStory.messageRecord.isFailed) {
|
||||||
lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe()
|
lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe()
|
||||||
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
|
||||||
|
@ -67,7 +76,8 @@ class MyStoriesFragment : DSLSettingsFragment(
|
||||||
Recipient.self().id
|
Recipient.self().id
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivity(StoryViewerActivity.createIntent(requireContext(), recipientId, conversationMessage.messageRecord.id))
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), preview, ViewCompat.getTransitionName(preview) ?: "")
|
||||||
|
startActivity(StoryViewerActivity.createIntent(requireContext(), recipientId, conversationMessage.messageRecord.id), options.toBundle())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSaveClick = {
|
onSaveClick = {
|
||||||
|
|
|
@ -32,7 +32,7 @@ object MyStoriesItem {
|
||||||
|
|
||||||
class Model(
|
class Model(
|
||||||
val distributionStory: ConversationMessage,
|
val distributionStory: ConversationMessage,
|
||||||
val onClick: (Model) -> Unit,
|
val onClick: (Model, View) -> Unit,
|
||||||
val onSaveClick: (Model) -> Unit,
|
val onSaveClick: (Model) -> Unit,
|
||||||
val onDeleteClick: (Model) -> Unit,
|
val onDeleteClick: (Model) -> Unit,
|
||||||
val onForwardClick: (Model) -> Unit,
|
val onForwardClick: (Model) -> Unit,
|
||||||
|
@ -81,7 +81,7 @@ object MyStoriesItem {
|
||||||
|
|
||||||
override fun bind(model: Model) {
|
override fun bind(model: Model) {
|
||||||
storyPreview.isClickable = false
|
storyPreview.isClickable = false
|
||||||
itemView.setOnClickListener { model.onClick(model) }
|
itemView.setOnClickListener { model.onClick(model, storyPreview) }
|
||||||
downloadTarget.setOnClickListener { model.onSaveClick(model) }
|
downloadTarget.setOnClickListener { model.onSaveClick(model) }
|
||||||
moreTarget.setOnClickListener { showContextMenu(model) }
|
moreTarget.setOnClickListener { showContextMenu(model) }
|
||||||
presentDateOrStatus(model)
|
presentDateOrStatus(model)
|
||||||
|
|
|
@ -16,6 +16,8 @@ class StoryViewerActivity : PassphraseRequiredActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
supportPostponeEnterTransition()
|
||||||
|
|
||||||
super.onCreate(savedInstanceState, ready)
|
super.onCreate(savedInstanceState, ready)
|
||||||
setContentView(R.layout.fragment_container)
|
setContentView(R.layout.fragment_container)
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,7 @@
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
|
android:transitionName="story"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
android:layout_height="84dp"
|
android:layout_height="84dp"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:transitionName="story"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
|
@ -12,12 +12,11 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Story.Text" />
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Story.Text" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.stories.StoryTextView
|
||||||
android:id="@+id/text_story_post_text"
|
android:id="@+id/text_story_post_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:layout_gravity="start|center_vertical"
|
||||||
android:background="@drawable/rounded_rectangle_secondary_18"
|
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingHorizontal="12dp"
|
android:paddingHorizontal="12dp"
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
android:textSize="34dp"
|
android:textSize="34dp"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="SpUsage"
|
tools:ignore="SpUsage"
|
||||||
|
|
|
@ -10,17 +10,18 @@
|
||||||
android:id="@+id/story_content_card_touch_interceptor"
|
android:id="@+id/story_content_card_touch_interceptor"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintVertical_bias="0"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="9:16"
|
app:layout_constraintDimensionRatio="9:16"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/story_content_card"
|
android:id="@+id/story_content_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:transitionName="story"
|
||||||
app:cardCornerRadius="18dp"
|
app:cardCornerRadius="18dp"
|
||||||
app:cardElevation="0dp">
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<transition class="org.thoughtcrime.securesms.stories.ScaleTransition" />
|
||||||
|
</transitionSet>
|
|
@ -16,6 +16,12 @@
|
||||||
<item name="android:colorControlActivated">@color/core_ultramarine_light</item>
|
<item name="android:colorControlActivated">@color/core_ultramarine_light</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="TextSecure.DarkNoActionBar.StoryViewer">
|
||||||
|
<item name="android:windowContentTransitions">true</item>
|
||||||
|
<item name="android:windowSharedElementEnterTransition">@transition/change_transform</item>
|
||||||
|
<item name="android:windowSharedElementExitTransition">@transition/change_transform</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="TextSecure.LightNoActionBar" parent="TextSecure.BaseLightNoActionBar">
|
<style name="TextSecure.LightNoActionBar" parent="TextSecure.BaseLightNoActionBar">
|
||||||
<item name="android:statusBarColor">@color/core_grey_60</item>
|
<item name="android:statusBarColor">@color/core_grey_60</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -35,6 +35,9 @@
|
||||||
<!-- leave empty to allow overriding -->
|
<!-- leave empty to allow overriding -->
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="TextSecure.DarkNoActionBar.StoryViewer">
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="TextSecure.BaseMediaPreview" parent="@style/TextSecure.BaseDarkNoActionBar">
|
<style name="TextSecure.BaseMediaPreview" parent="@style/TextSecure.BaseDarkNoActionBar">
|
||||||
<item name="colorPrimaryDark">@color/media_preview_bar_background</item>
|
<item name="colorPrimaryDark">@color/media_preview_bar_background</item>
|
||||||
<item name="android:textColorSecondary">@color/white</item>
|
<item name="android:textColorSecondary">@color/white</item>
|
||||||
|
|
Ładowanie…
Reference in New Issue