mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Update first time navigation screen.
This commit is contained in:
@@ -516,6 +516,14 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Clear first time navigation state"),
|
||||
isEnabled = true,
|
||||
onClick = {
|
||||
SignalStore.storyValues().userHasSeenFirstNavView = false
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__internal_stories_dialog_launcher),
|
||||
onClick = {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.thoughtcrime.securesms.stories
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
@@ -10,14 +11,13 @@ import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import org.signal.core.util.DimensionUnit
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.blurhash.BlurHash
|
||||
import org.thoughtcrime.securesms.components.CornerMask
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
|
||||
@@ -28,20 +28,22 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
||||
|
||||
companion object {
|
||||
private const val BLUR_ALPHA = 0x3D
|
||||
private const val NO_BLUR_ALPHA = 0xB3
|
||||
private const val NO_BLUR_ALPHA = 0xCC
|
||||
}
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.story_first_time_navigation_view, this)
|
||||
}
|
||||
|
||||
private val tapToAdvance: LottieAnimationView = findViewById(R.id.edu_tap_icon)
|
||||
private val swipeUp: LottieAnimationView = findViewById(R.id.edu_swipe_up_icon)
|
||||
private val swipeRight: LottieAnimationView = findViewById(R.id.edu_swipe_right_icon)
|
||||
private val blurHashView: ImageView = findViewById(R.id.edu_blur_hash)
|
||||
private val overlayView: ImageView = findViewById(R.id.edu_overlay)
|
||||
private val gotIt: View = findViewById(R.id.edu_got_it)
|
||||
private val close: View = findViewById(R.id.edu_close)
|
||||
|
||||
private val cornerMask = CornerMask(this).apply {
|
||||
setRadius(DimensionUnit.DP.toPixels(18f).toInt())
|
||||
}
|
||||
private var isPlayingAnimations = false
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
@@ -59,12 +61,14 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
||||
hide()
|
||||
}
|
||||
|
||||
setOnClickListener { }
|
||||
}
|
||||
close.setOnClickListener {
|
||||
callback?.onCloseClicked()
|
||||
GlideApp.with(this).clear(blurHashView)
|
||||
blurHashView.setImageDrawable(null)
|
||||
hide()
|
||||
}
|
||||
|
||||
override fun dispatchDraw(canvas: Canvas) {
|
||||
super.dispatchDraw(canvas)
|
||||
cornerMask.mask(canvas)
|
||||
setOnClickListener { }
|
||||
}
|
||||
|
||||
fun setBlurHash(blurHash: BlurHash?) {
|
||||
@@ -105,10 +109,47 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
visible = true
|
||||
startLottieAnimations()
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
visible = false
|
||||
endLottieAnimations()
|
||||
}
|
||||
|
||||
private fun startLottieAnimations() {
|
||||
isPlayingAnimations = true
|
||||
tapToAdvance.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
if (isPlayingAnimations) {
|
||||
swipeUp.playAnimation()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
swipeUp.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
if (isPlayingAnimations) {
|
||||
swipeRight.playAnimation()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
swipeRight.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
postDelayed({
|
||||
if (isPlayingAnimations) {
|
||||
startLottieAnimations()
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
})
|
||||
|
||||
tapToAdvance.playAnimation()
|
||||
}
|
||||
|
||||
private fun endLottieAnimations() {
|
||||
isPlayingAnimations = false
|
||||
}
|
||||
|
||||
private fun isRenderEffectSupported(): Boolean {
|
||||
@@ -118,5 +159,6 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
||||
interface Callback {
|
||||
fun userHasSeenFirstNavigationView(): Boolean
|
||||
fun onGotItClicked()
|
||||
fun onCloseClicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.media.AudioManagerCompat
|
||||
@@ -16,7 +17,9 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
||||
import org.thoughtcrime.securesms.util.FullscreenHelper
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -34,9 +37,18 @@ class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControll
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
StoryMutePolicy.initialize()
|
||||
Glide.get(this).setMemoryCategory(MemoryCategory.HIGH)
|
||||
FullscreenHelper.showSystemUI(window)
|
||||
|
||||
supportPostponeEnterTransition()
|
||||
|
||||
val root = findViewById<View>(android.R.id.content)
|
||||
root.setPadding(
|
||||
0,
|
||||
ViewUtil.getStatusBarHeight(root),
|
||||
0,
|
||||
ViewUtil.getNavigationBarHeight(root)
|
||||
)
|
||||
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
setContentView(R.layout.fragment_container)
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.thoughtcrime.securesms.stories.viewer
|
||||
|
||||
import android.graphics.RenderEffect
|
||||
import android.graphics.Shader
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
@@ -8,9 +11,11 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
||||
import org.thoughtcrime.securesms.stories.viewer.first.StoryFirstTimeNavigationFragment
|
||||
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageArgs
|
||||
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageFragment
|
||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoriesSharedElementCrossFaderView
|
||||
@@ -116,6 +121,20 @@ class StoryViewerFragment :
|
||||
viewModel.addHiddenAndRefresh(ids.toSet())
|
||||
} else {
|
||||
viewModel.refresh()
|
||||
|
||||
if (!SignalStore.storyValues().userHasSeenFirstNavView) {
|
||||
StoryFirstTimeNavigationFragment().show(childFragmentManager, null)
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
lifecycleDisposable += viewModel.isFirstTimeNavigationShowing.subscribe {
|
||||
if (it) {
|
||||
requireView().rootView.setRenderEffect(RenderEffect.createBlurEffect(100f, 100f, Shader.TileMode.CLAMP))
|
||||
} else {
|
||||
requireView().rootView.setRenderEffect(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,13 +53,20 @@ class StoryViewerViewModel(
|
||||
var hasConsumedInitialState = false
|
||||
private set
|
||||
|
||||
private val firstTimeNavigationPublisher: BehaviorSubject<Boolean> = BehaviorSubject.createDefault(false)
|
||||
|
||||
val isChildScrolling: Observable<Boolean> = childScrollStatePublisher.distinctUntilChanged()
|
||||
val isFirstTimeNavigationShowing: Observable<Boolean> = firstTimeNavigationPublisher.distinctUntilChanged()
|
||||
|
||||
fun addHiddenAndRefresh(hidden: Set<RecipientId>) {
|
||||
this.hidden.addAll(hidden)
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setIsDisplayingFirstTimeNavigation(isDisplayingFirstTimeNavigation: Boolean) {
|
||||
firstTimeNavigationPublisher.onNext(isDisplayingFirstTimeNavigation)
|
||||
}
|
||||
|
||||
fun getHidden(): Set<RecipientId> = hidden
|
||||
|
||||
fun setCrossfadeTarget(messageRecord: MmsMessageRecord) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.thoughtcrime.securesms.stories.viewer.first
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.stories.StoryFirstTimeNavigationView
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerState
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerViewModel
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
|
||||
class StoryFirstTimeNavigationFragment : DialogFragment(R.layout.story_viewer_first_time_navigation_stub), StoryFirstTimeNavigationView.Callback {
|
||||
|
||||
private val viewModel: StoryViewerViewModel by viewModels(ownerProducer = {
|
||||
requireParentFragment()
|
||||
})
|
||||
|
||||
private val disposables = LifecycleDisposable()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen)
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
dialog.window!!.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION or
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or
|
||||
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
)
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
(view as StoryFirstTimeNavigationView).show()
|
||||
view.callback = this
|
||||
viewModel.setIsDisplayingFirstTimeNavigation(true)
|
||||
|
||||
disposables += viewModel.state.subscribe { state ->
|
||||
when (state.crossfadeSource) {
|
||||
is StoryViewerState.CrossfadeSource.ImageUri -> {
|
||||
view.setBlurHash(state.crossfadeSource.imageBlur)
|
||||
}
|
||||
else -> {
|
||||
view.setBlurHash(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun userHasSeenFirstNavigationView(): Boolean {
|
||||
return SignalStore.storyValues().userHasSeenFirstNavView
|
||||
}
|
||||
|
||||
override fun onGotItClicked() {
|
||||
dismissAllowingStateLoss()
|
||||
|
||||
SignalStore.storyValues().userHasSeenFirstNavView = true
|
||||
viewModel.setIsDisplayingFirstTimeNavigation(false)
|
||||
}
|
||||
|
||||
override fun onCloseClicked() {
|
||||
dismissAllowingStateLoss()
|
||||
|
||||
if (viewModel.stateSnapshot.skipCrossfade) {
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
ActivityCompat.finishAfterTransition(requireActivity())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package org.thoughtcrime.securesms.stories.viewer.page
|
||||
|
||||
import android.view.ViewStub
|
||||
import androidx.core.view.isVisible
|
||||
import org.thoughtcrime.securesms.blurhash.BlurHash
|
||||
import org.thoughtcrime.securesms.stories.StoryFirstTimeNavigationView
|
||||
import org.thoughtcrime.securesms.util.views.Stub
|
||||
|
||||
/**
|
||||
* Specialized stub that allows for early arrival of the blurhash and callback.
|
||||
*/
|
||||
class StoryFirstNavigationStub(viewStub: ViewStub) : Stub<StoryFirstTimeNavigationView>(viewStub) {
|
||||
|
||||
private var callback: StoryFirstTimeNavigationView.Callback? = null
|
||||
private var blurHash: BlurHash? = null
|
||||
|
||||
fun setCallback(callback: StoryFirstTimeNavigationView.Callback) {
|
||||
if (resolved()) {
|
||||
get().callback = callback
|
||||
} else {
|
||||
this.callback = callback
|
||||
}
|
||||
}
|
||||
|
||||
fun setBlurHash(blurHash: BlurHash?) {
|
||||
if (resolved()) {
|
||||
get().setBlurHash(blurHash)
|
||||
} else {
|
||||
this.blurHash = blurHash
|
||||
}
|
||||
}
|
||||
|
||||
fun showIfAble(ableToShow: Boolean) {
|
||||
if (ableToShow) {
|
||||
get().show()
|
||||
}
|
||||
}
|
||||
|
||||
fun isVisible(): Boolean {
|
||||
return resolved() && get().isVisible
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
if (resolved()) {
|
||||
get().hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(): StoryFirstTimeNavigationView {
|
||||
val needsResolve = !resolved()
|
||||
val view = super.get()
|
||||
if (needsResolve) {
|
||||
view.setBlurHash(blurHash)
|
||||
view.callback = callback
|
||||
blurHash = null
|
||||
callback = null
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,8 @@ import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.RenderEffect
|
||||
import android.graphics.Shader
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.GestureDetector
|
||||
@@ -55,14 +52,12 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewFragment
|
||||
import org.thoughtcrime.securesms.mediapreview.VideoControlsDelegate
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
|
||||
import org.thoughtcrime.securesms.stories.StoryFirstTimeNavigationView
|
||||
import org.thoughtcrime.securesms.stories.StorySlateView
|
||||
import org.thoughtcrime.securesms.stories.StoryVolumeOverlayView
|
||||
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
|
||||
@@ -97,7 +92,6 @@ class StoryViewerPageFragment :
|
||||
StoryPostFragment.Callback,
|
||||
MultiselectForwardBottomSheet.Callback,
|
||||
StorySlateView.Callback,
|
||||
StoryFirstTimeNavigationView.Callback,
|
||||
StoryInfoBottomSheetDialogFragment.OnInfoSheetDismissedListener,
|
||||
SafetyNumberBottomSheet.Callbacks {
|
||||
|
||||
@@ -108,7 +102,7 @@ class StoryViewerPageFragment :
|
||||
private lateinit var viewsAndReplies: MaterialButton
|
||||
private lateinit var storyCaptionContainer: FrameLayout
|
||||
private lateinit var storyContentContainer: FrameLayout
|
||||
private lateinit var storyFirstTimeNavigationViewStub: StoryFirstNavigationStub
|
||||
private lateinit var storyPageContainer: ConstraintLayout
|
||||
private lateinit var sendingBarTextView: TextView
|
||||
private lateinit var sendingBar: View
|
||||
|
||||
@@ -176,17 +170,16 @@ class StoryViewerPageFragment :
|
||||
val storyGradientBottom: View = view.findViewById(R.id.story_gradient_bottom)
|
||||
val storyVolumeOverlayView: StoryVolumeOverlayView = view.findViewById(R.id.story_volume_overlay)
|
||||
|
||||
storyPageContainer = view.findViewById(R.id.story_page_container)
|
||||
storyContentContainer = view.findViewById(R.id.story_content_container)
|
||||
storyCaptionContainer = view.findViewById(R.id.story_caption_container)
|
||||
storySlate = view.findViewById(R.id.story_slate)
|
||||
progressBar = view.findViewById(R.id.progress)
|
||||
viewsAndReplies = view.findViewById(R.id.views_and_replies_bar)
|
||||
storyFirstTimeNavigationViewStub = StoryFirstNavigationStub(view.findViewById(R.id.story_first_time_nav_stub))
|
||||
sendingBarTextView = view.findViewById(R.id.sending_text_view)
|
||||
sendingBar = view.findViewById(R.id.sending_bar)
|
||||
|
||||
storySlate.callback = this
|
||||
storyFirstTimeNavigationViewStub.setCallback(this)
|
||||
|
||||
chrome = listOf(
|
||||
closeView,
|
||||
@@ -322,6 +315,10 @@ class StoryViewerPageFragment :
|
||||
viewModel.setIsUserScrollingChild(it)
|
||||
}
|
||||
|
||||
lifecycleDisposable += sharedViewModel.isFirstTimeNavigationShowing.subscribe {
|
||||
viewModel.setIsDisplayingFirstTimeNavigation(it)
|
||||
}
|
||||
|
||||
lifecycleDisposable += storyVolumeViewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe { volumeState ->
|
||||
if (volumeState.isMuted) {
|
||||
videoControlsDelegate.mute()
|
||||
@@ -384,7 +381,6 @@ class StoryViewerPageFragment :
|
||||
presentDate(date, post)
|
||||
presentDistributionList(distributionList, post)
|
||||
presentCaption(caption, largeCaption, largeCaptionOverlay, post)
|
||||
presentBlur(post)
|
||||
|
||||
val durations: Map<Int, Long> = state.posts
|
||||
.mapIndexed { index, storyPost ->
|
||||
@@ -428,37 +424,20 @@ class StoryViewerPageFragment :
|
||||
resumeProgress()
|
||||
}
|
||||
|
||||
val wasDisplayingNavigationView = storyFirstTimeNavigationViewStub.isVisible()
|
||||
|
||||
when {
|
||||
state.hideChromeImmediate -> {
|
||||
hideChromeImmediate()
|
||||
storyCaptionContainer.visible = false
|
||||
storyFirstTimeNavigationViewStub.hide()
|
||||
}
|
||||
state.hideChrome -> {
|
||||
hideChrome()
|
||||
storyCaptionContainer.visible = true
|
||||
storyFirstTimeNavigationViewStub.showIfAble(!SignalStore.storyValues().userHasSeenFirstNavView)
|
||||
}
|
||||
else -> {
|
||||
showChrome()
|
||||
storyCaptionContainer.visible = true
|
||||
storyFirstTimeNavigationViewStub.showIfAble(!SignalStore.storyValues().userHasSeenFirstNavView)
|
||||
}
|
||||
}
|
||||
|
||||
val isDisplayingNavigationView = storyFirstTimeNavigationViewStub.isVisible()
|
||||
if (isDisplayingNavigationView && Build.VERSION.SDK_INT >= 31) {
|
||||
hideChromeImmediate()
|
||||
storyContentContainer.setRenderEffect(RenderEffect.createBlurEffect(100f, 100f, Shader.TileMode.CLAMP))
|
||||
} else if (Build.VERSION.SDK_INT >= 31) {
|
||||
storyContentContainer.setRenderEffect(null)
|
||||
}
|
||||
|
||||
if (wasDisplayingNavigationView xor isDisplayingNavigationView) {
|
||||
viewModel.setIsDisplayingFirstTimeNavigation(storyFirstTimeNavigationViewStub.isVisible())
|
||||
}
|
||||
}
|
||||
|
||||
timeoutDisposable.bindTo(viewLifecycleOwner)
|
||||
@@ -590,7 +569,7 @@ class StoryViewerPageFragment :
|
||||
card: CardView
|
||||
) {
|
||||
val constraintSet = ConstraintSet()
|
||||
constraintSet.clone(requireView() as ConstraintLayout)
|
||||
constraintSet.clone(storyPageContainer)
|
||||
|
||||
when (StoryDisplay.getStoryDisplay(resources.displayMetrics.widthPixels.toFloat(), resources.displayMetrics.heightPixels.toFloat())) {
|
||||
StoryDisplay.LARGE -> {
|
||||
@@ -613,7 +592,7 @@ class StoryViewerPageFragment :
|
||||
}
|
||||
}
|
||||
|
||||
constraintSet.applyTo(requireView() as ConstraintLayout)
|
||||
constraintSet.applyTo(storyPageContainer)
|
||||
}
|
||||
|
||||
private fun resumeProgress() {
|
||||
@@ -781,13 +760,6 @@ class StoryViewerPageFragment :
|
||||
distributionList.visible = storyPost.distributionList != null && !storyPost.distributionList.isMyStory
|
||||
}
|
||||
|
||||
private fun presentBlur(storyPost: StoryPost) {
|
||||
val record = storyPost.conversationMessage.messageRecord as? MediaMmsMessageRecord
|
||||
val blurHash = record?.slideDeck?.thumbnailSlide?.placeholderBlur
|
||||
|
||||
storyFirstTimeNavigationViewStub.setBlurHash(blurHash)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun presentCaption(caption: TextView, largeCaption: TextView, largeCaptionOverlay: View, storyPost: StoryPost) {
|
||||
val displayBody: String = if (storyPost.content is StoryPost.Content.AttachmentContent) {
|
||||
@@ -1284,15 +1256,6 @@ class StoryViewerPageFragment :
|
||||
sharedViewModel.setContentIsReady()
|
||||
}
|
||||
|
||||
override fun userHasSeenFirstNavigationView(): Boolean {
|
||||
return SignalStore.storyValues().userHasSeenFirstNavView
|
||||
}
|
||||
|
||||
override fun onGotItClicked() {
|
||||
SignalStore.storyValues().userHasSeenFirstNavView = true
|
||||
viewModel.setIsDisplayingFirstTimeNavigation(false)
|
||||
}
|
||||
|
||||
override fun onInfoSheetDismissed() {
|
||||
viewModel.setIsDisplayingInfoDialog(false)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ data class StoryViewerPlaybackState(
|
||||
val isUserScaling: Boolean = false,
|
||||
val isDisplayingPartialSendDialog: Boolean = false
|
||||
) {
|
||||
val hideChromeImmediate: Boolean = isRunningSharedElementAnimation
|
||||
val hideChromeImmediate: Boolean = isRunningSharedElementAnimation || isDisplayingFirstTimeNavigation
|
||||
|
||||
val hideChrome: Boolean = isRunningSharedElementAnimation ||
|
||||
isUserLongTouching ||
|
||||
|
||||
13
app/src/main/res/layout/stories_viewer_activity.xml
Normal file
13
app/src/main/res/layout/stories_viewer_activity.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -2,6 +2,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/story_page_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
@@ -295,13 +296,4 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/story_first_time_nav_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:inflatedId="@+id/story_first_time_navigation_view"
|
||||
android:layout="@layout/story_viewer_first_time_navigation_stub"
|
||||
app:layout_constraintBottom_toBottomOf="@id/story_content_card_touch_interceptor"
|
||||
app:layout_constraintTop_toTopOf="@id/story_content_card_touch_interceptor" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -8,115 +8,95 @@
|
||||
<ImageView
|
||||
android:id="@+id/edu_blur_hash"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no" />
|
||||
android:layout_height="0dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintDimensionRatio="9:16"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@color/transparent_black_70" />
|
||||
android:src="@color/transparent_black_80" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/button_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_got_it"
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/edu_tap_icon"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_tap_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:lottie_rawRes="@raw/stories_tap_to_advance" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_tap_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_tap_label"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_tap_72"
|
||||
app:tint="@color/core_white" />
|
||||
<TextView
|
||||
android:id="@+id/edu_tap_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/StoryFirstTimeNavigationView__tap_to_advance"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_tap_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_tap_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__tap_to_advance"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_tap_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_tap_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_tap_icon" />
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/edu_swipe_up_icon"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_up_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_tap_label"
|
||||
app:lottie_rawRes="@raw/stories_swipe_up" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_up_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_up_label"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_tap_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_up_72"
|
||||
app:tint="@color/core_white" />
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_up_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_up_to_skip"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_up_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_up_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_up_to_skip"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_up_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_up_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edu_swipe_right_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/edu_swipe_right_label"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_up_icon"
|
||||
app:srcCompat="@drawable/ic_swipe_right_72"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_right_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_right_to_exit"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edu_swipe_right_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/edu_swipe_right_icon" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/edu_swipe_right_icon"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/edu_swipe_right_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_up_label"
|
||||
app:lottie_rawRes="@raw/stories_swipe_right" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edu_swipe_right_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/StoryFirstTimeNavigationView__swipe_right_to_exit"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_right_icon" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/edu_got_it"
|
||||
@@ -129,6 +109,17 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_container" />
|
||||
app:layout_constraintTop_toBottomOf="@id/edu_swipe_right_label" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/edu_close"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:contentDescription="@string/Material3SearchToolbar__close"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_x_white" />
|
||||
</merge>
|
||||
@@ -2,8 +2,5 @@
|
||||
<org.thoughtcrime.securesms.stories.StoryFirstTimeNavigationView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/story_content_card_touch_interceptor"
|
||||
app:layout_constraintTop_toTopOf="@id/story_content_card_touch_interceptor" />
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
1
app/src/main/res/raw/stories_swipe_right.json
Normal file
1
app/src/main/res/raw/stories_swipe_right.json
Normal file
File diff suppressed because one or more lines are too long
1
app/src/main/res/raw/stories_swipe_up.json
Normal file
1
app/src/main/res/raw/stories_swipe_up.json
Normal file
File diff suppressed because one or more lines are too long
1
app/src/main/res/raw/stories_tap_to_advance.json
Normal file
1
app/src/main/res/raw/stories_tap_to_advance.json
Normal file
File diff suppressed because one or more lines are too long
@@ -212,7 +212,7 @@
|
||||
|
||||
<style name="Signal.DayNight.Dialog.FullScreen.Donate">
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Signal.Media.Dialog.AddMessageDialog" parent="TextSecure.MediaPreview">
|
||||
<item name="android:windowAnimationStyle">@style/TextSecure.Animation.AddMessageDialog</item>
|
||||
</style>
|
||||
|
||||
@@ -117,6 +117,7 @@ class StoryFirstTimeNavigationViewTest {
|
||||
testSubject.callback = object : StoryFirstTimeNavigationView.Callback {
|
||||
override fun userHasSeenFirstNavigationView(): Boolean = true
|
||||
override fun onGotItClicked() = error("Unused")
|
||||
override fun onCloseClicked() = error("Unused")
|
||||
}
|
||||
|
||||
testSubject.setBlurHash(BlurHash.parseOrNull("0000")!!)
|
||||
@@ -150,6 +151,7 @@ class StoryFirstTimeNavigationViewTest {
|
||||
testSubject.callback = object : StoryFirstTimeNavigationView.Callback {
|
||||
override fun userHasSeenFirstNavigationView(): Boolean = true
|
||||
override fun onGotItClicked() = error("Unused")
|
||||
override fun onCloseClicked() = error("Unused")
|
||||
}
|
||||
|
||||
testSubject.show()
|
||||
|
||||
Reference in New Issue
Block a user