Update first time navigation screen.

main
Alex Hart 2022-10-27 13:43:52 -03:00 zatwierdzone przez GitHub
rodzic d003dc435a
commit 3600a4818c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
18 zmienionych plików z 303 dodań i 229 usunięć

Wyświetl plik

@ -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 = {

Wyświetl plik

@ -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()
}
}

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)
}
}
}
}

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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())
}
}
}

Wyświetl plik

@ -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
}
}

Wyświetl plik

@ -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)
}

Wyświetl plik

@ -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 ||

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -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>

Wyświetl plik

@ -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()