kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add dialog protection and remote deletion to disabling stories and deleting lists.
rodzic
ad1801108d
commit
4b94509a7a
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,38 @@ import org.thoughtcrime.securesms.R
|
|||
|
||||
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) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)
|
||||
|
|
|
@ -10,6 +10,8 @@ import androidx.navigation.fragment.NavHostFragment
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
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.settings.DSLConfiguration
|
||||
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.database.model.DistributionListId
|
||||
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.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||
|
@ -28,6 +31,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
|||
menuId = R.menu.story_private_menu
|
||||
) {
|
||||
|
||||
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
|
||||
|
||||
private val viewModel: PrivateStorySettingsViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
PrivateStorySettingsViewModel.Factory(PrivateStorySettingsFragmentArgs.fromBundle(requireArguments()).distributionListId, PrivateStorySettingsRepository())
|
||||
|
@ -49,6 +54,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
|||
val toolbar: Toolbar = requireView().findViewById(R.id.toolbar)
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||
if (state.isActionInProgress) {
|
||||
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
|
||||
} else {
|
||||
progressDisplayManager.hide()
|
||||
}
|
||||
|
||||
toolbar.title = state.privateStory?.name
|
||||
adapter.submitList(getConfiguration(state).toMappingModelList())
|
||||
}
|
||||
|
@ -88,7 +99,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
|||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.PrivateStorySettingsFragment__delete_private_story, DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_alert_primary))),
|
||||
onClick = {
|
||||
handleDeletePrivateStory()
|
||||
val privateStoryName = viewModel.state.value?.privateStory?.name
|
||||
handleDeletePrivateStory(privateStoryName)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -113,13 +125,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
|||
.show()
|
||||
}
|
||||
|
||||
private fun handleDeletePrivateStory() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PrivateStorySettingsFragment__are_you_sure)
|
||||
.setMessage(R.string.PrivateStorySettingsFragment__this_action_cannot)
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.setPositiveButton(R.string.delete) { _, _ -> viewModel.delete().subscribe { findNavController().popBackStack() } }
|
||||
.show()
|
||||
private fun handleDeletePrivateStory(privateStoryName: String?) {
|
||||
val name = privateStoryName ?: return
|
||||
|
||||
StoryDialogs.deleteDistributionList(requireContext(), name) {
|
||||
viewModel.delete().subscribe { findNavController().popBackStack() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onToolbarNavigationClicked() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
|||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListRecord
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
|
||||
class PrivateStorySettingsRepository {
|
||||
|
@ -27,6 +28,13 @@ class PrivateStorySettingsRepository {
|
|||
return Completable.fromAction {
|
||||
SignalDatabase.distributionLists.deleteList(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())
|
||||
}
|
||||
|
||||
|
|
|
@ -4,5 +4,6 @@ import org.thoughtcrime.securesms.database.model.DistributionListRecord
|
|||
|
||||
data class PrivateStorySettingsState(
|
||||
val privateStory: DistributionListRecord? = null,
|
||||
val areRepliesAndReactionsEnabled: Boolean = false
|
||||
val areRepliesAndReactionsEnabled: Boolean = false,
|
||||
val isActionInProgress: Boolean = false
|
||||
)
|
||||
|
|
|
@ -52,7 +52,9 @@ class PrivateStorySettingsViewModel(private val distributionListId: Distribution
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -4,9 +4,10 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.signal.core.util.dp
|
||||
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.DSLSettingsAdapter
|
||||
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.mediasend.v2.stories.ChooseGroupStoryBottomSheet
|
||||
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.CreateStoryWithViewersFragment
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||
|
@ -36,6 +38,7 @@ class StoriesPrivacySettingsFragment :
|
|||
|
||||
private val viewModel: StoriesPrivacySettingsViewModel by viewModels()
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
|
||||
|
||||
override fun createAdapters(): Array<MappingAdapter> {
|
||||
return arrayOf(DSLSettingsAdapter(), PagingMappingAdapter<ContactSearchKey>(), DSLSettingsAdapter())
|
||||
|
@ -84,6 +87,12 @@ class StoriesPrivacySettingsFragment :
|
|||
}
|
||||
|
||||
lifecycleDisposable += viewModel.state.subscribe { state ->
|
||||
if (state.isUpdatingEnabledState) {
|
||||
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
|
||||
} else {
|
||||
progressDisplayManager.hide()
|
||||
}
|
||||
|
||||
(top as MappingAdapter).submitList(getTopConfiguration(state).toMappingModelList())
|
||||
middle.submitList(getMiddleConfiguration(state).toMappingModelList())
|
||||
(bottom as MappingAdapter).submitList(getBottomConfiguration(state).toMappingModelList())
|
||||
|
@ -144,12 +153,9 @@ class StoriesPrivacySettingsFragment :
|
|||
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
||||
),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.StoriesPrivacySettingsFragment__turn_off_stories_question)
|
||||
.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()
|
||||
StoryDialogs.disableStories(requireContext(), viewModel.userHasActiveStories) {
|
||||
viewModel.setStoriesEnabled(false)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package org.thoughtcrime.securesms.stories.settings.story
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
|
||||
|
@ -23,6 +25,20 @@ class StoriesPrivacySettingsRepository {
|
|||
return Completable.fromAction {
|
||||
SignalStore.storyValues().isFeatureDisabled = !isEnabled
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchData
|
|||
data class StoriesPrivacySettingsState(
|
||||
val areStoriesEnabled: Boolean,
|
||||
val isUpdatingEnabledState: Boolean = false,
|
||||
val storyContactItems: List<ContactSearchData> = emptyList()
|
||||
val storyContactItems: List<ContactSearchData> = emptyList(),
|
||||
val userHasStories: Boolean = false
|
||||
)
|
||||
|
|
|
@ -39,6 +39,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
|||
private val headerActionRequestSubject = PublishSubject.create<Unit>()
|
||||
|
||||
val state: Flowable<StoriesPrivacySettingsState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
|
||||
val userHasActiveStories: Boolean get() = store.state.userHasStories
|
||||
val pagingController = ProxyPagingController<ContactSearchKey>()
|
||||
val headerActionRequests: Observable<Unit> = headerActionRequestSubject.debounce(100, TimeUnit.MILLISECONDS)
|
||||
|
||||
|
@ -59,6 +60,8 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
|||
|
||||
pagingController.set(observablePagedData.controller)
|
||||
|
||||
updateUserHasStories()
|
||||
|
||||
disposables += store.update(observablePagedData.data.toFlowable(BackpressureStrategy.LATEST)) { data, state ->
|
||||
state.copy(storyContactItems = data)
|
||||
}
|
||||
|
@ -78,6 +81,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
|||
areStoriesEnabled = Stories.isFeatureEnabled()
|
||||
)
|
||||
}
|
||||
updateUserHasStories()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,4 +90,10 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
|||
pagingController.onDataInvalidated()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUserHasStories() {
|
||||
disposables += repository.userHasOutgoingStories().subscribe { userHasActiveStories ->
|
||||
store.update { it.copy(userHasStories = userHasActiveStories) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,6 @@
|
|||
android:layout_gravity="center"
|
||||
android:layout_margin="24dp"
|
||||
android:indeterminate="true"
|
||||
android:background="@color/transparent"
|
||||
app:indicatorColor="@color/signal_colorPrimary" />
|
||||
</merge>
|
||||
|
|
|
@ -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>
|
|
@ -2753,6 +2753,9 @@
|
|||
<string name="configurable_single_select__customize_option">Customize option</string>
|
||||
|
||||
<!-- 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_story_or_profile_selector" translatable="false">Story or profile selector</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>
|
||||
<!-- Done button label for hide story from screen -->
|
||||
<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 -->
|
||||
<string name="StoryDialogs__add_to_story_q">Add to story?</string>
|
||||
<!-- 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>
|
||||
<!-- Error message dialog button to resend a previously failed story send -->
|
||||
<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 -->
|
||||
<string name="PrivacySettingsFragment__share_and_view_stories">Share & View Stories</string>
|
||||
<!-- Privacy Settings toggle summary for stories -->
|
||||
|
@ -5231,7 +5242,7 @@
|
|||
<!-- Dialog title to turn off stories -->
|
||||
<string name="StoriesPrivacySettingsFragment__turn_off_stories_question">Turn off stories?</string>
|
||||
<!-- 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 -->
|
||||
<string name="StoriesPrivacySettingsFragment__story_privacy">Story privacy</string>
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue