kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix story viewed state retention.
rodzic
4e3bfadfbe
commit
5d4d6db197
|
@ -17,6 +17,8 @@ import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
||||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
||||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
||||||
|
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewStateCache
|
||||||
|
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewStateViewModel
|
||||||
import org.thoughtcrime.securesms.util.FullscreenHelper
|
import org.thoughtcrime.securesms.util.FullscreenHelper
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
|
@ -26,6 +28,7 @@ import kotlin.math.min
|
||||||
class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner {
|
class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner {
|
||||||
|
|
||||||
private val viewModel: StoryVolumeViewModel by viewModels()
|
private val viewModel: StoryVolumeViewModel by viewModels()
|
||||||
|
private val storyViewStateViewModel: StoryViewStateViewModel by viewModels()
|
||||||
|
|
||||||
override lateinit var voiceNoteMediaController: VoiceNoteMediaController
|
override lateinit var voiceNoteMediaController: VoiceNoteMediaController
|
||||||
|
|
||||||
|
@ -35,6 +38,13 @@ class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControll
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
val cache: StoryViewStateCache? = savedInstanceState.getParcelable(DATA_CACHE)
|
||||||
|
if (cache != null) {
|
||||||
|
storyViewStateViewModel.storyViewStateCache.putAll(cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StoryMutePolicy.initialize()
|
StoryMutePolicy.initialize()
|
||||||
Glide.get(this).setMemoryCategory(MemoryCategory.HIGH)
|
Glide.get(this).setMemoryCategory(MemoryCategory.HIGH)
|
||||||
FullscreenHelper.showSystemUI(window)
|
FullscreenHelper.showSystemUI(window)
|
||||||
|
@ -59,6 +69,11 @@ class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putParcelable(DATA_CACHE, storyViewStateViewModel.storyViewStateCache)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
Glide.get(this).setMemoryCategory(MemoryCategory.NORMAL)
|
Glide.get(this).setMemoryCategory(MemoryCategory.NORMAL)
|
||||||
|
@ -115,6 +130,7 @@ class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControll
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARGS = "story.viewer.args"
|
private const val ARGS = "story.viewer.args"
|
||||||
|
private const val DATA_CACHE = "story.viewer.cache"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createIntent(
|
fun createIntent(
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package org.thoughtcrime.securesms.stories.viewer.page
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import org.thoughtcrime.securesms.util.ParcelUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity-bounds ViewModel which tracks the viewed state for stories.
|
||||||
|
*/
|
||||||
|
class StoryViewStateCache() : Parcelable {
|
||||||
|
|
||||||
|
private val viewStateMap: MutableMap<Long, Boolean> = mutableMapOf()
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this() {
|
||||||
|
synchronized(this) {
|
||||||
|
val entries: Collection<Entry> = ParcelUtil.readParcelableCollection(parcel, Entry::class.java)
|
||||||
|
entries.forEach {
|
||||||
|
viewStateMap[it.storyId] = it.hasSelfViewed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putAll(cache: StoryViewStateCache) {
|
||||||
|
synchronized(this) {
|
||||||
|
viewStateMap.putAll(cache.viewStateMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If storyId is in our map, return its value. Otherwise, insert and return the given state.
|
||||||
|
*/
|
||||||
|
fun getOrPut(storyId: Long, hasSelfViewed: Boolean): Boolean {
|
||||||
|
synchronized(this) {
|
||||||
|
return if (viewStateMap.containsKey(storyId)) {
|
||||||
|
viewStateMap[storyId]!!
|
||||||
|
} else {
|
||||||
|
viewStateMap[storyId] = hasSelfViewed
|
||||||
|
hasSelfViewed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
ParcelUtil.writeParcelableCollection(parcel, viewStateMap.map { Entry(it.key, it.value) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<StoryViewStateCache> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): StoryViewStateCache {
|
||||||
|
return StoryViewStateCache(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<StoryViewStateCache?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Entry(
|
||||||
|
val storyId: Long,
|
||||||
|
val hasSelfViewed: Boolean
|
||||||
|
) : Parcelable
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.thoughtcrime.securesms.stories.viewer.page
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class StoryViewStateViewModel : ViewModel() {
|
||||||
|
val storyViewStateCache = StoryViewStateCache()
|
||||||
|
}
|
|
@ -115,12 +115,15 @@ class StoryViewerPageFragment :
|
||||||
private var volumeOutAnimator: Animator? = null
|
private var volumeOutAnimator: Animator? = null
|
||||||
private var volumeDebouncer: Debouncer = Debouncer(3, TimeUnit.SECONDS)
|
private var volumeDebouncer: Debouncer = Debouncer(3, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
private val storyViewStateViewModel: StoryViewStateViewModel by viewModels()
|
||||||
|
|
||||||
private val viewModel: StoryViewerPageViewModel by viewModels(
|
private val viewModel: StoryViewerPageViewModel by viewModels(
|
||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
StoryViewerPageViewModel.Factory(
|
StoryViewerPageViewModel.Factory(
|
||||||
storyViewerPageArgs,
|
storyViewerPageArgs,
|
||||||
StoryViewerPageRepository(
|
StoryViewerPageRepository(
|
||||||
requireContext()
|
requireContext(),
|
||||||
|
storyViewStateViewModel.storyViewStateCache
|
||||||
),
|
),
|
||||||
StoryCache(
|
StoryCache(
|
||||||
GlideApp.with(requireActivity()),
|
GlideApp.with(requireActivity()),
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.util.Base64
|
||||||
/**
|
/**
|
||||||
* Open for testing.
|
* Open for testing.
|
||||||
*/
|
*/
|
||||||
open class StoryViewerPageRepository(context: Context) {
|
open class StoryViewerPageRepository(context: Context, private val storyViewStateCache: StoryViewStateCache) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = Log.tag(StoryViewerPageRepository::class.java)
|
private val TAG = Log.tag(StoryViewerPageRepository::class.java)
|
||||||
|
@ -88,7 +88,7 @@ open class StoryViewerPageRepository(context: Context) {
|
||||||
content = getContent(record as MmsMessageRecord),
|
content = getContent(record as MmsMessageRecord),
|
||||||
conversationMessage = ConversationMessage.ConversationMessageFactory.createWithUnresolvedData(context, record),
|
conversationMessage = ConversationMessage.ConversationMessageFactory.createWithUnresolvedData(context, record),
|
||||||
allowsReplies = record.storyType.isStoryWithReplies,
|
allowsReplies = record.storyType.isStoryWithReplies,
|
||||||
hasSelfViewed = if (record.isOutgoing) true else record.viewedReceiptCount > 0
|
hasSelfViewed = storyViewStateCache.getOrPut(record.id, if (record.isOutgoing) true else record.viewedReceiptCount > 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
emitter.onNext(story)
|
emitter.onNext(story)
|
||||||
|
|
Ładowanie…
Reference in New Issue