Add dialog protection and remote deletion to disabling stories and deleting lists.

fork-5.53.8
Alex Hart 2022-10-05 15:04:54 -03:00 zatwierdzone przez GitHub
rodzic ad1801108d
commit 4b94509a7a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
15 zmienionych plików z 212 dodań i 19 usunięć

Wyświetl plik

@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.components
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
/**
* Manages the lifecycle of displaying a dialog fragment. Will automatically close and nullify the reference
* if the bound lifecycle is destroyed, and handles repeat calls to show such that no more than one dialog is
* displayed.
*/
class DialogFragmentDisplayManager(private val builder: () -> DialogFragment) : DefaultLifecycleObserver {
private var dialogFragment: DialogFragment? = null
fun show(lifecycleOwner: LifecycleOwner, fragmentManager: FragmentManager, tag: String? = null) {
val fragment = dialogFragment ?: builder()
if (fragment.dialog?.isShowing != true) {
fragment.show(fragmentManager, tag)
dialogFragment = fragment
lifecycleOwner.lifecycle.addObserver(this)
}
}
fun hide() {
dialogFragment?.dismissNow()
dialogFragment = null
}
override fun onDestroy(owner: LifecycleOwner) {
owner.lifecycle.removeObserver(this)
hide()
}
}

Wyświetl plik

@ -0,0 +1,20 @@
package org.thoughtcrime.securesms.components
import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import org.thoughtcrime.securesms.R
/**
* Displays a small progress spinner in a card view, as a non-cancellable dialog fragment.
*/
class ProgressCardDialogFragment : DialogFragment(R.layout.progress_card_dialog) {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
isCancelable = false
return super.onCreateDialog(savedInstanceState).apply {
this.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
}
}

Wyświetl plik

@ -44,6 +44,33 @@ class StoryDialogLauncherFragment : DSLSettingsFragment(titleId = R.string.prefe
} }
} }
) )
clickPref(
title = DSLSettingsText.from(R.string.preferences__internal_turn_off_stories),
onClick = {
StoryDialogs.disableStories(requireContext(), false) {
Toast.makeText(requireContext(), R.string.preferences__internal_turn_off_stories, Toast.LENGTH_SHORT).show()
}
}
)
clickPref(
title = DSLSettingsText.from(R.string.preferences__internal_turn_off_stories_with_stories_on_disk),
onClick = {
StoryDialogs.disableStories(requireContext(), true) {
Toast.makeText(requireContext(), R.string.preferences__internal_turn_off_stories_with_stories_on_disk, Toast.LENGTH_SHORT).show()
}
}
)
clickPref(
title = DSLSettingsText.from(R.string.preferences__internal_delete_private_story),
onClick = {
StoryDialogs.deleteDistributionList(requireContext(), "Family") {
Toast.makeText(requireContext(), R.string.preferences__internal_delete_private_story, Toast.LENGTH_SHORT).show()
}
}
)
} }
} }
} }

Wyświetl plik

@ -10,6 +10,38 @@ import org.thoughtcrime.securesms.R
object StoryDialogs { object StoryDialogs {
fun deleteDistributionList(
context: Context,
distributionListName: String,
onDelete: () -> Unit
) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.StoryDialogs__delete_private_story)
.setMessage(context.getString(R.string.StoryDialogs__s_and_updates_shared, distributionListName))
.setPositiveButton(R.string.StoryDialogs__delete) { _, _ -> onDelete() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
fun disableStories(
context: Context,
userHasStories: Boolean,
onDisable: () -> Unit
) {
val positiveButtonMessage = if (userHasStories) {
R.string.StoryDialogs__turn_off_and_delete
} else {
R.string.StoriesPrivacySettingsFragment__turn_off_stories
}
MaterialAlertDialogBuilder(context)
.setTitle(R.string.StoriesPrivacySettingsFragment__turn_off_stories_question)
.setMessage(R.string.StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to_share)
.setPositiveButton(positiveButtonMessage) { _, _ -> onDisable() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
fun resendStory(context: Context, onDismiss: () -> Unit = {}, resend: () -> Unit) { fun resendStory(context: Context, onDismiss: () -> Unit = {}, resend: () -> Unit) {
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(context)
.setMessage(R.string.StoryDialogs__story_could_not_be_sent) .setMessage(R.string.StoryDialogs__story_could_not_be_sent)

Wyświetl plik

@ -10,6 +10,8 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.DialogFragmentDisplayManager
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
import org.thoughtcrime.securesms.components.WrapperDialogFragment import org.thoughtcrime.securesms.components.WrapperDialogFragment
import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
@ -17,6 +19,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.fragments.findListener import org.thoughtcrime.securesms.util.fragments.findListener
@ -28,6 +31,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
menuId = R.menu.story_private_menu menuId = R.menu.story_private_menu
) { ) {
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
private val viewModel: PrivateStorySettingsViewModel by viewModels( private val viewModel: PrivateStorySettingsViewModel by viewModels(
factoryProducer = { factoryProducer = {
PrivateStorySettingsViewModel.Factory(PrivateStorySettingsFragmentArgs.fromBundle(requireArguments()).distributionListId, PrivateStorySettingsRepository()) PrivateStorySettingsViewModel.Factory(PrivateStorySettingsFragmentArgs.fromBundle(requireArguments()).distributionListId, PrivateStorySettingsRepository())
@ -49,6 +54,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
val toolbar: Toolbar = requireView().findViewById(R.id.toolbar) val toolbar: Toolbar = requireView().findViewById(R.id.toolbar)
viewModel.state.observe(viewLifecycleOwner) { state -> viewModel.state.observe(viewLifecycleOwner) { state ->
if (state.isActionInProgress) {
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
} else {
progressDisplayManager.hide()
}
toolbar.title = state.privateStory?.name toolbar.title = state.privateStory?.name
adapter.submitList(getConfiguration(state).toMappingModelList()) adapter.submitList(getConfiguration(state).toMappingModelList())
} }
@ -88,7 +99,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
clickPref( clickPref(
title = DSLSettingsText.from(R.string.PrivateStorySettingsFragment__delete_private_story, DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_alert_primary))), title = DSLSettingsText.from(R.string.PrivateStorySettingsFragment__delete_private_story, DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_alert_primary))),
onClick = { onClick = {
handleDeletePrivateStory() val privateStoryName = viewModel.state.value?.privateStory?.name
handleDeletePrivateStory(privateStoryName)
} }
) )
} }
@ -113,13 +125,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
.show() .show()
} }
private fun handleDeletePrivateStory() { private fun handleDeletePrivateStory(privateStoryName: String?) {
MaterialAlertDialogBuilder(requireContext()) val name = privateStoryName ?: return
.setTitle(R.string.PrivateStorySettingsFragment__are_you_sure)
.setMessage(R.string.PrivateStorySettingsFragment__this_action_cannot) StoryDialogs.deleteDistributionList(requireContext(), name) {
.setNegativeButton(android.R.string.cancel) { _, _ -> } viewModel.delete().subscribe { findNavController().popBackStack() }
.setPositiveButton(R.string.delete) { _, _ -> viewModel.delete().subscribe { findNavController().popBackStack() } } }
.show()
} }
override fun onToolbarNavigationClicked() { override fun onToolbarNavigationClicked() {

Wyświetl plik

@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.DistributionListRecord import org.thoughtcrime.securesms.database.model.DistributionListRecord
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.stories.Stories
class PrivateStorySettingsRepository { class PrivateStorySettingsRepository {
@ -27,6 +28,13 @@ class PrivateStorySettingsRepository {
return Completable.fromAction { return Completable.fromAction {
SignalDatabase.distributionLists.deleteList(distributionListId) SignalDatabase.distributionLists.deleteList(distributionListId)
Stories.onStorySettingsChanged(distributionListId) Stories.onStorySettingsChanged(distributionListId)
val recipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(distributionListId)
SignalDatabase.mms.getAllStoriesFor(recipientId, -1).use { reader ->
for (record in reader) {
MessageSender.sendRemoteDelete(record.id, record.isMms)
}
}
}.subscribeOn(Schedulers.io()) }.subscribeOn(Schedulers.io())
} }

Wyświetl plik

@ -4,5 +4,6 @@ import org.thoughtcrime.securesms.database.model.DistributionListRecord
data class PrivateStorySettingsState( data class PrivateStorySettingsState(
val privateStory: DistributionListRecord? = null, val privateStory: DistributionListRecord? = null,
val areRepliesAndReactionsEnabled: Boolean = false val areRepliesAndReactionsEnabled: Boolean = false,
val isActionInProgress: Boolean = false
) )

Wyświetl plik

@ -52,7 +52,9 @@ class PrivateStorySettingsViewModel(private val distributionListId: Distribution
} }
fun delete(): Completable { fun delete(): Completable {
return repository.delete(distributionListId).observeOn(AndroidSchedulers.mainThread()) return repository.delete(distributionListId)
.doOnSubscribe { store.update { it.copy(isActionInProgress = true) } }
.observeOn(AndroidSchedulers.mainThread())
} }
class Factory(private val privateStoryItemData: DistributionListId, private val repository: PrivateStorySettingsRepository) : ViewModelProvider.Factory { class Factory(private val privateStoryItemData: DistributionListId, private val repository: PrivateStorySettingsRepository) : ViewModelProvider.Factory {

Wyświetl plik

@ -4,9 +4,10 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.ConcatAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.signal.core.util.dp import org.signal.core.util.dp
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.DialogFragmentDisplayManager
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
@ -17,6 +18,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.groups.ParcelableGroupId import org.thoughtcrime.securesms.groups.ParcelableGroupId
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomSheet import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomSheet
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil import org.thoughtcrime.securesms.util.BottomSheetUtil
@ -36,6 +38,7 @@ class StoriesPrivacySettingsFragment :
private val viewModel: StoriesPrivacySettingsViewModel by viewModels() private val viewModel: StoriesPrivacySettingsViewModel by viewModels()
private val lifecycleDisposable = LifecycleDisposable() private val lifecycleDisposable = LifecycleDisposable()
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
override fun createAdapters(): Array<MappingAdapter> { override fun createAdapters(): Array<MappingAdapter> {
return arrayOf(DSLSettingsAdapter(), PagingMappingAdapter<ContactSearchKey>(), DSLSettingsAdapter()) return arrayOf(DSLSettingsAdapter(), PagingMappingAdapter<ContactSearchKey>(), DSLSettingsAdapter())
@ -84,6 +87,12 @@ class StoriesPrivacySettingsFragment :
} }
lifecycleDisposable += viewModel.state.subscribe { state -> lifecycleDisposable += viewModel.state.subscribe { state ->
if (state.isUpdatingEnabledState) {
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
} else {
progressDisplayManager.hide()
}
(top as MappingAdapter).submitList(getTopConfiguration(state).toMappingModelList()) (top as MappingAdapter).submitList(getTopConfiguration(state).toMappingModelList())
middle.submitList(getMiddleConfiguration(state).toMappingModelList()) middle.submitList(getMiddleConfiguration(state).toMappingModelList())
(bottom as MappingAdapter).submitList(getBottomConfiguration(state).toMappingModelList()) (bottom as MappingAdapter).submitList(getBottomConfiguration(state).toMappingModelList())
@ -144,12 +153,9 @@ class StoriesPrivacySettingsFragment :
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant)) DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
), ),
onClick = { onClick = {
MaterialAlertDialogBuilder(requireContext()) StoryDialogs.disableStories(requireContext(), viewModel.userHasActiveStories) {
.setTitle(R.string.StoriesPrivacySettingsFragment__turn_off_stories_question) viewModel.setStoriesEnabled(false)
.setMessage(R.string.StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to) }
.setPositiveButton(R.string.StoriesPrivacySettingsFragment__turn_off_stories) { _, _ -> viewModel.setStoriesEnabled(false) }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
} }
) )
} }

Wyświetl plik

@ -1,12 +1,14 @@
package org.thoughtcrime.securesms.stories.settings.story package org.thoughtcrime.securesms.stories.settings.story
import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.storage.StorageSyncHelper import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.stories.Stories
@ -23,6 +25,20 @@ class StoriesPrivacySettingsRepository {
return Completable.fromAction { return Completable.fromAction {
SignalStore.storyValues().isFeatureDisabled = !isEnabled SignalStore.storyValues().isFeatureDisabled = !isEnabled
Stories.onStorySettingsChanged(Recipient.self().id) Stories.onStorySettingsChanged(Recipient.self().id)
SignalDatabase.mms.getAllOutgoingStories(false, -1).use { reader ->
reader.map { record -> record.id }
}.forEach { messageId ->
MessageSender.sendRemoteDelete(messageId, true)
}
}.subscribeOn(Schedulers.io())
}
fun userHasOutgoingStories(): Single<Boolean> {
return Single.fromCallable {
SignalDatabase.mms.getAllOutgoingStories(false, -1).use {
it.iterator().hasNext()
}
}.subscribeOn(Schedulers.io()) }.subscribeOn(Schedulers.io())
} }
} }

Wyświetl plik

@ -5,5 +5,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchData
data class StoriesPrivacySettingsState( data class StoriesPrivacySettingsState(
val areStoriesEnabled: Boolean, val areStoriesEnabled: Boolean,
val isUpdatingEnabledState: Boolean = false, val isUpdatingEnabledState: Boolean = false,
val storyContactItems: List<ContactSearchData> = emptyList() val storyContactItems: List<ContactSearchData> = emptyList(),
val userHasStories: Boolean = false
) )

Wyświetl plik

@ -39,6 +39,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
private val headerActionRequestSubject = PublishSubject.create<Unit>() private val headerActionRequestSubject = PublishSubject.create<Unit>()
val state: Flowable<StoriesPrivacySettingsState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread()) val state: Flowable<StoriesPrivacySettingsState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
val userHasActiveStories: Boolean get() = store.state.userHasStories
val pagingController = ProxyPagingController<ContactSearchKey>() val pagingController = ProxyPagingController<ContactSearchKey>()
val headerActionRequests: Observable<Unit> = headerActionRequestSubject.debounce(100, TimeUnit.MILLISECONDS) val headerActionRequests: Observable<Unit> = headerActionRequestSubject.debounce(100, TimeUnit.MILLISECONDS)
@ -59,6 +60,8 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
pagingController.set(observablePagedData.controller) pagingController.set(observablePagedData.controller)
updateUserHasStories()
disposables += store.update(observablePagedData.data.toFlowable(BackpressureStrategy.LATEST)) { data, state -> disposables += store.update(observablePagedData.data.toFlowable(BackpressureStrategy.LATEST)) { data, state ->
state.copy(storyContactItems = data) state.copy(storyContactItems = data)
} }
@ -78,6 +81,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
areStoriesEnabled = Stories.isFeatureEnabled() areStoriesEnabled = Stories.isFeatureEnabled()
) )
} }
updateUserHasStories()
} }
} }
@ -86,4 +90,10 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
pagingController.onDataInvalidated() pagingController.onDataInvalidated()
} }
} }
private fun updateUserHasStories() {
disposables += repository.userHasOutgoingStories().subscribe { userHasActiveStories ->
store.update { it.copy(userHasStories = userHasActiveStories) }
}
}
} }

Wyświetl plik

@ -12,5 +12,6 @@
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="24dp" android:layout_margin="24dp"
android:indeterminate="true" android:indeterminate="true"
android:background="@color/transparent"
app:indicatorColor="@color/signal_colorPrimary" /> app:indicatorColor="@color/signal_colorPrimary" />
</merge> </merge>

Wyświetl plik

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<org.thoughtcrime.securesms.components.ProgressCard
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="18dp" />
</FrameLayout>

Wyświetl plik

@ -2753,6 +2753,9 @@
<string name="configurable_single_select__customize_option">Customize option</string> <string name="configurable_single_select__customize_option">Customize option</string>
<!-- Internal only preferences --> <!-- Internal only preferences -->
<string name="preferences__internal_turn_off_stories_with_stories_on_disk" translatable="false">Turn off stories (with stories on disk)</string>
<string name="preferences__internal_turn_off_stories" translatable="false">Turn off stories</string>
<string name="preferences__internal_delete_private_story" translatable="false">Delete private story</string>
<string name="preferences__internal_hide_story" translatable="false">Hide story</string> <string name="preferences__internal_hide_story" translatable="false">Hide story</string>
<string name="preferences__internal_story_or_profile_selector" translatable="false">Story or profile selector</string> <string name="preferences__internal_story_or_profile_selector" translatable="false">Story or profile selector</string>
<string name="preferences__internal_stories_dialog_launcher" translatable="false">Stories dialog launcher</string> <string name="preferences__internal_stories_dialog_launcher" translatable="false">Stories dialog launcher</string>
@ -4936,6 +4939,12 @@
<string name="ChangeMyStoryMembershipFragment__only_share_with">Only share with…</string> <string name="ChangeMyStoryMembershipFragment__only_share_with">Only share with…</string>
<!-- Done button label for hide story from screen --> <!-- Done button label for hide story from screen -->
<string name="HideStoryFromFragment__done">Done</string> <string name="HideStoryFromFragment__done">Done</string>
<!-- Dialog title for deleting a private story -->
<string name="StoryDialogs__delete_private_story">Delete private story?</string>
<!-- Dialog message for deleting a private story -->
<string name="StoryDialogs__s_and_updates_shared">\"%1$s\" and updates shared to this story will be deleted.</string>
<!-- Dialog positive action for deleting a private story -->
<string name="StoryDialogs__delete">Delete</string>
<!-- Dialog title for first time adding something to a story --> <!-- Dialog title for first time adding something to a story -->
<string name="StoryDialogs__add_to_story_q">Add to story?</string> <string name="StoryDialogs__add_to_story_q">Add to story?</string>
<!-- Dialog message for first time adding something to a story --> <!-- Dialog message for first time adding something to a story -->
@ -4948,6 +4957,8 @@
<string name="StoryDialogs__story_could_not_be_sent">Story could not be sent. Check your connection and try again.</string> <string name="StoryDialogs__story_could_not_be_sent">Story could not be sent. Check your connection and try again.</string>
<!-- Error message dialog button to resend a previously failed story send --> <!-- Error message dialog button to resend a previously failed story send -->
<string name="StoryDialogs__send">Send</string> <string name="StoryDialogs__send">Send</string>
<!-- Action button for turning off stories when stories are present on the device -->
<string name="StoryDialogs__turn_off_and_delete">Turn off and delete</string>
<!-- Privacy Settings toggle title for stories --> <!-- Privacy Settings toggle title for stories -->
<string name="PrivacySettingsFragment__share_and_view_stories">Share &amp; View Stories</string> <string name="PrivacySettingsFragment__share_and_view_stories">Share &amp; View Stories</string>
<!-- Privacy Settings toggle summary for stories --> <!-- Privacy Settings toggle summary for stories -->
@ -5231,7 +5242,7 @@
<!-- Dialog title to turn off stories --> <!-- Dialog title to turn off stories -->
<string name="StoriesPrivacySettingsFragment__turn_off_stories_question">Turn off stories?</string> <string name="StoriesPrivacySettingsFragment__turn_off_stories_question">Turn off stories?</string>
<!-- Dialog message to turn off stories --> <!-- Dialog message to turn off stories -->
<string name="StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to">You will no longer be able to share or view stories. Any stories you have recently sent will still be visible by others until they expire.</string> <string name="StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to_share">You will no longer be able to share or view stories. Story updates you have recently shared will also be deleted.</string>
<!-- Page title when launched from stories landing screen --> <!-- Page title when launched from stories landing screen -->
<string name="StoriesPrivacySettingsFragment__story_privacy">Story privacy</string> <string name="StoriesPrivacySettingsFragment__story_privacy">Story privacy</string>