kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add first time My Story privacy configuration.
rodzic
3eac397263
commit
78d4d9a3dd
|
@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
import org.thoughtcrime.securesms.components.FromTextView
|
import org.thoughtcrime.securesms.components.FromTextView
|
||||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||||
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
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
|
||||||
|
@ -55,7 +56,7 @@ object ContactSearchItems {
|
||||||
return MappingModelList(
|
return MappingModelList(
|
||||||
contactSearchData.filterNotNull().map {
|
contactSearchData.filterNotNull().map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ContactSearchData.Story -> StoryModel(it, selection.contains(it.contactSearchKey))
|
is ContactSearchData.Story -> StoryModel(it, selection.contains(it.contactSearchKey), SignalStore.storyValues().userHasBeenNotifiedAboutStories)
|
||||||
is ContactSearchData.KnownRecipient -> RecipientModel(it, selection.contains(it.contactSearchKey))
|
is ContactSearchData.KnownRecipient -> RecipientModel(it, selection.contains(it.contactSearchKey))
|
||||||
is ContactSearchData.Expand -> ExpandModel(it)
|
is ContactSearchData.Expand -> ExpandModel(it)
|
||||||
is ContactSearchData.Header -> HeaderModel(it)
|
is ContactSearchData.Header -> HeaderModel(it)
|
||||||
|
@ -67,18 +68,23 @@ object ContactSearchItems {
|
||||||
/**
|
/**
|
||||||
* Story Model
|
* Story Model
|
||||||
*/
|
*/
|
||||||
private class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean) : MappingModel<StoryModel> {
|
private class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean, val hasBeenNotified: Boolean) : MappingModel<StoryModel> {
|
||||||
|
|
||||||
override fun areItemsTheSame(newItem: StoryModel): Boolean {
|
override fun areItemsTheSame(newItem: StoryModel): Boolean {
|
||||||
return newItem.story == story
|
return newItem.story == story
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(newItem: StoryModel): Boolean {
|
override fun areContentsTheSame(newItem: StoryModel): Boolean {
|
||||||
return story.recipient.hasSameContent(newItem.story.recipient) && isSelected == newItem.isSelected
|
return story.recipient.hasSameContent(newItem.story.recipient) &&
|
||||||
|
isSelected == newItem.isSelected &&
|
||||||
|
hasBeenNotified == newItem.hasBeenNotified
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChangePayload(newItem: StoryModel): Any? {
|
override fun getChangePayload(newItem: StoryModel): Any? {
|
||||||
return if (story.recipient.hasSameContent(newItem.story.recipient) && newItem.isSelected != isSelected) {
|
return if (story.recipient.hasSameContent(newItem.story.recipient) &&
|
||||||
|
hasBeenNotified == newItem.hasBeenNotified &&
|
||||||
|
newItem.isSelected != isSelected
|
||||||
|
) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
@ -100,6 +106,9 @@ object ContactSearchItems {
|
||||||
model.story.viewerCount
|
model.story.viewerCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model.story.recipient.isMyStory && !model.hasBeenNotified) {
|
||||||
|
number.setText(R.string.ContactSearchItems__tap_to_choose_your_viewers)
|
||||||
|
} else {
|
||||||
val pluralId = when {
|
val pluralId = when {
|
||||||
model.story.recipient.isGroup -> R.plurals.ContactSearchItems__group_story_d_viewers
|
model.story.recipient.isGroup -> R.plurals.ContactSearchItems__group_story_d_viewers
|
||||||
model.story.recipient.isMyStory -> R.plurals.SelectViewersFragment__d_viewers
|
model.story.recipient.isMyStory -> R.plurals.SelectViewersFragment__d_viewers
|
||||||
|
@ -108,6 +117,7 @@ object ContactSearchItems {
|
||||||
|
|
||||||
number.text = context.resources.getQuantityString(pluralId, count, count)
|
number.text = context.resources.getQuantityString(pluralId, count, count)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun bindLongPress(model: StoryModel) {
|
override fun bindLongPress(model: StoryModel) {
|
||||||
itemView.setOnLongClickListener {
|
itemView.setOnLongClickListener {
|
||||||
|
|
|
@ -9,8 +9,10 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
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.groups.SelectionLimits
|
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.stories.settings.custom.PrivateStorySettingsFragment
|
import org.thoughtcrime.securesms.stories.settings.custom.PrivateStorySettingsFragment
|
||||||
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsFragment
|
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsFragment
|
||||||
|
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil
|
import org.thoughtcrime.securesms.util.SpanUtil
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter
|
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
||||||
|
@ -35,7 +37,7 @@ class ContactSearchMediator(
|
||||||
mappingAdapter = adapter,
|
mappingAdapter = adapter,
|
||||||
displayCheckBox = displayCheckBox,
|
displayCheckBox = displayCheckBox,
|
||||||
recipientListener = this::toggleSelection,
|
recipientListener = this::toggleSelection,
|
||||||
storyListener = this::toggleSelection,
|
storyListener = this::toggleStorySelection,
|
||||||
storyContextMenuCallbacks = StoryContextMenuCallbacks(),
|
storyContextMenuCallbacks = StoryContextMenuCallbacks(),
|
||||||
expandListener = { viewModel.expandSection(it.sectionKey) }
|
expandListener = { viewModel.expandSection(it.sectionKey) }
|
||||||
)
|
)
|
||||||
|
@ -87,6 +89,14 @@ class ContactSearchMediator(
|
||||||
viewModel.refresh()
|
viewModel.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun toggleStorySelection(view: View, contactSearchData: ContactSearchData.Story, isSelected: Boolean) {
|
||||||
|
if (contactSearchData.recipient.isMyStory && !SignalStore.storyValues().userHasBeenNotifiedAboutStories) {
|
||||||
|
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.show(fragment.childFragmentManager)
|
||||||
|
} else {
|
||||||
|
toggleSelection(view, contactSearchData, isSelected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun toggleSelection(view: View, contactSearchData: ContactSearchData, isSelected: Boolean) {
|
private fun toggleSelection(view: View, contactSearchData: ContactSearchData, isSelected: Boolean) {
|
||||||
return if (isSelected) {
|
return if (isSelected) {
|
||||||
viewModel.setKeysNotSelected(setOf(contactSearchData.contactSearchKey))
|
viewModel.setKeysNotSelected(setOf(contactSearchData.contactSearchKey))
|
||||||
|
|
|
@ -45,10 +45,9 @@ import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
|
||||||
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
import org.thoughtcrime.securesms.stories.Stories.getHeaderAction
|
import org.thoughtcrime.securesms.stories.Stories.getHeaderAction
|
||||||
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.stories.settings.privacy.HideStoryFromDialogFragment
|
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||||
import org.thoughtcrime.securesms.util.FullscreenHelper
|
import org.thoughtcrime.securesms.util.FullscreenHelper
|
||||||
|
@ -77,7 +76,8 @@ class MultiselectForwardFragment :
|
||||||
Fragment(R.layout.multiselect_forward_fragment),
|
Fragment(R.layout.multiselect_forward_fragment),
|
||||||
SafetyNumberChangeDialog.Callback,
|
SafetyNumberChangeDialog.Callback,
|
||||||
ChooseStoryTypeBottomSheet.Callback,
|
ChooseStoryTypeBottomSheet.Callback,
|
||||||
WrapperDialogFragment.WrapperDialogFragmentCallback {
|
WrapperDialogFragment.WrapperDialogFragmentCallback,
|
||||||
|
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.Callback {
|
||||||
|
|
||||||
private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory)
|
private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory)
|
||||||
private val disposables = LifecycleDisposable()
|
private val disposables = LifecycleDisposable()
|
||||||
|
@ -285,24 +285,6 @@ class MultiselectForwardFragment :
|
||||||
|
|
||||||
private fun onSend(sendButton: View) {
|
private fun onSend(sendButton: View) {
|
||||||
sendButton.isEnabled = false
|
sendButton.isEnabled = false
|
||||||
|
|
||||||
StoryDialogs.guardWithAddToYourStoryDialog(
|
|
||||||
requireContext(),
|
|
||||||
contactSearchMediator.getSelectedContacts(),
|
|
||||||
onAddToStory = {
|
|
||||||
performSend()
|
|
||||||
},
|
|
||||||
onEditViewers = {
|
|
||||||
sendButton.isEnabled = true
|
|
||||||
HideStoryFromDialogFragment().show(childFragmentManager, null)
|
|
||||||
},
|
|
||||||
onCancel = {
|
|
||||||
sendButton.isEnabled = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performSend() {
|
|
||||||
viewModel.send(addMessage.text.toString(), contactSearchMediator.getSelectedContacts())
|
viewModel.send(addMessage.text.toString(), contactSearchMediator.getSelectedContacts())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +465,15 @@ class MultiselectForwardFragment :
|
||||||
CreateStoryFlowDialogFragment().show(parentFragmentManager, CreateStoryWithViewersFragment.REQUEST_KEY)
|
CreateStoryFlowDialogFragment().show(parentFragmentManager, CreateStoryWithViewersFragment.REQUEST_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onWrapperDialogFragmentDismissed() {
|
||||||
|
contactSearchMediator.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMyStoryConfigured(recipientId: RecipientId) {
|
||||||
|
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
|
||||||
|
contactSearchMediator.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
fun onFinishForwardAction()
|
fun onFinishForwardAction()
|
||||||
fun exitFlow()
|
fun exitFlow()
|
||||||
|
@ -544,8 +535,4 @@ class MultiselectForwardFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWrapperDialogFragmentDismissed() {
|
|
||||||
contactSearchMediator.refresh()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendRepository
|
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendRepository
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendResult
|
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendResult
|
||||||
import org.thoughtcrime.securesms.stories.StoryTextPostView
|
import org.thoughtcrime.securesms.stories.StoryTextPostView
|
||||||
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
|
||||||
import org.thoughtcrime.securesms.stories.settings.privacy.HideStoryFromDialogFragment
|
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||||
|
|
||||||
|
@ -134,22 +132,7 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
|
||||||
findNavController().safeNavigate(R.id.action_textStoryPostCreationFragment_to_textStoryPostSendFragment)
|
findNavController().safeNavigate(R.id.action_textStoryPostCreationFragment_to_textStoryPostSendFragment)
|
||||||
} else {
|
} else {
|
||||||
send.isClickable = false
|
send.isClickable = false
|
||||||
StoryDialogs.guardWithAddToYourStoryDialog(
|
|
||||||
contacts = contacts,
|
|
||||||
context = requireContext(),
|
|
||||||
onAddToStory = {
|
|
||||||
performSend(contacts)
|
performSend(contacts)
|
||||||
},
|
|
||||||
onEditViewers = {
|
|
||||||
send.isClickable = true
|
|
||||||
storyTextPostView.hideCloseButton()
|
|
||||||
HideStoryFromDialogFragment().show(childFragmentManager, null)
|
|
||||||
},
|
|
||||||
onCancel = {
|
|
||||||
send.isClickable = true
|
|
||||||
storyTextPostView.hideCloseButton()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,19 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
|
import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
|
||||||
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
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.stories.settings.privacy.HideStoryFromDialogFragment
|
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
||||||
|
|
||||||
class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragment), ChooseStoryTypeBottomSheet.Callback, WrapperDialogFragment.WrapperDialogFragmentCallback {
|
class TextStoryPostSendFragment :
|
||||||
|
Fragment(R.layout.stories_send_text_post_fragment),
|
||||||
|
ChooseStoryTypeBottomSheet.Callback,
|
||||||
|
WrapperDialogFragment.WrapperDialogFragmentCallback,
|
||||||
|
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.Callback {
|
||||||
|
|
||||||
private lateinit var shareListWrapper: View
|
private lateinit var shareListWrapper: View
|
||||||
private lateinit var shareSelectionRecyclerView: RecyclerView
|
private lateinit var shareSelectionRecyclerView: RecyclerView
|
||||||
|
@ -87,18 +90,7 @@ class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragm
|
||||||
|
|
||||||
shareConfirmButton.setOnClickListener {
|
shareConfirmButton.setOnClickListener {
|
||||||
viewModel.onSending()
|
viewModel.onSending()
|
||||||
StoryDialogs.guardWithAddToYourStoryDialog(
|
send()
|
||||||
contacts = contactSearchMediator.getSelectedContacts(),
|
|
||||||
context = requireContext(),
|
|
||||||
onAddToStory = { send() },
|
|
||||||
onEditViewers = {
|
|
||||||
viewModel.onSendCancelled()
|
|
||||||
HideStoryFromDialogFragment().show(childFragmentManager, null)
|
|
||||||
},
|
|
||||||
onCancel = {
|
|
||||||
viewModel.onSendCancelled()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disposables += viewModel.untrustedIdentities.subscribe {
|
disposables += viewModel.untrustedIdentities.subscribe {
|
||||||
|
@ -200,4 +192,9 @@ class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragm
|
||||||
override fun onWrapperDialogFragmentDismissed() {
|
override fun onWrapperDialogFragmentDismissed() {
|
||||||
contactSearchMediator.refresh()
|
contactSearchMediator.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onMyStoryConfigured(recipientId: RecipientId) {
|
||||||
|
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
|
||||||
|
contactSearchMediator.refresh()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,53 +2,14 @@ package org.thoughtcrime.securesms.stories.dialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import com.google.android.material.shape.ShapeAppearanceModel
|
import com.google.android.material.shape.ShapeAppearanceModel
|
||||||
import org.signal.core.util.DimensionUnit
|
import org.signal.core.util.DimensionUnit
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
|
||||||
|
|
||||||
object StoryDialogs {
|
object StoryDialogs {
|
||||||
|
|
||||||
/**
|
|
||||||
* Guards onAddToStory with a dialog
|
|
||||||
*/
|
|
||||||
fun guardWithAddToYourStoryDialog(
|
|
||||||
context: Context,
|
|
||||||
contacts: Collection<ContactSearchKey>,
|
|
||||||
onAddToStory: () -> Unit,
|
|
||||||
onEditViewers: () -> Unit,
|
|
||||||
onCancel: () -> Unit = {}
|
|
||||||
) {
|
|
||||||
if (!isFirstSendToMyStory(contacts)) {
|
|
||||||
onAddToStory()
|
|
||||||
} else {
|
|
||||||
SignalStore.storyValues().userHasBeenNotifiedAboutStories = true
|
|
||||||
MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Signal_MaterialAlertDialog)
|
|
||||||
.setTitle(R.string.StoryDialogs__add_to_story_q)
|
|
||||||
.setMessage(R.string.StoryDialogs__adding_content)
|
|
||||||
.setPositiveButton(R.string.StoryDialogs__add_to_story) { _, _ ->
|
|
||||||
onAddToStory.invoke()
|
|
||||||
}
|
|
||||||
.setNeutralButton(R.string.StoryDialogs__edit_viewers) { _, _ -> Toast.makeText(context, "New flow coming soon", Toast.LENGTH_SHORT).show() }
|
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ -> onCancel.invoke() }
|
|
||||||
.setCancelable(false)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isFirstSendToMyStory(shareContacts: Collection<ContactSearchKey>): Boolean {
|
|
||||||
if (SignalStore.storyValues().userHasBeenNotifiedAboutStories) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return shareContacts.any { it is ContactSearchKey.RecipientSearchKey.Story && Recipient.resolved(it.recipientId).isMyStory }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resendStory(context: Context, resend: () -> Unit) {
|
fun resendStory(context: Context, resend: () -> Unit) {
|
||||||
MaterialAlertDialogBuilder(context)
|
MaterialAlertDialogBuilder(context)
|
||||||
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)
|
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)
|
||||||
|
|
|
@ -1,27 +1,35 @@
|
||||||
package org.thoughtcrime.securesms.stories.settings.my
|
package org.thoughtcrime.securesms.stories.settings.my
|
||||||
|
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import io.reactivex.rxjava3.core.Completable
|
import io.reactivex.rxjava3.core.Completable
|
||||||
|
import io.reactivex.rxjava3.core.Observable
|
||||||
import io.reactivex.rxjava3.core.Single
|
import io.reactivex.rxjava3.core.Single
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
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.DistributionListPrivacyData
|
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyData
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipState
|
||||||
|
|
||||||
class MyStorySettingsRepository {
|
class MyStorySettingsRepository {
|
||||||
|
|
||||||
fun getPrivacyState(): Single<MyStoryPrivacyState> {
|
fun getPrivacyState(): Single<MyStoryPrivacyState> {
|
||||||
return Single.fromCallable {
|
return Single.fromCallable {
|
||||||
val privacyData: DistributionListPrivacyData = SignalDatabase.distributionLists.getPrivacyData(DistributionListId.MY_STORY)
|
getStoryPrivacyState()
|
||||||
|
|
||||||
MyStoryPrivacyState(
|
|
||||||
privacyMode = privacyData.privacyMode,
|
|
||||||
connectionCount = if (privacyData.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) privacyData.rawMemberCount else privacyData.memberCount
|
|
||||||
)
|
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun observeChooseInitialPrivacy(): Observable<ChooseInitialMyStoryMembershipState> {
|
||||||
|
return Single.fromCallable { SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!! }
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.flatMapObservable { recipientId ->
|
||||||
|
Recipient.observable(recipientId)
|
||||||
|
.flatMap { Observable.just(ChooseInitialMyStoryMembershipState(recipientId = recipientId, privacyState = getStoryPrivacyState())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setPrivacyMode(privacyMode: DistributionListPrivacyMode): Completable {
|
fun setPrivacyMode(privacyMode: DistributionListPrivacyMode): Completable {
|
||||||
return Completable.fromAction {
|
return Completable.fromAction {
|
||||||
SignalDatabase.distributionLists.setPrivacyMode(DistributionListId.MY_STORY, privacyMode)
|
SignalDatabase.distributionLists.setPrivacyMode(DistributionListId.MY_STORY, privacyMode)
|
||||||
|
@ -41,4 +49,14 @@ class MyStorySettingsRepository {
|
||||||
Stories.onStorySettingsChanged(DistributionListId.MY_STORY)
|
Stories.onStorySettingsChanged(DistributionListId.MY_STORY)
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun getStoryPrivacyState(): MyStoryPrivacyState {
|
||||||
|
val privacyData: DistributionListPrivacyData = SignalDatabase.distributionLists.getPrivacyData(DistributionListId.MY_STORY)
|
||||||
|
|
||||||
|
return MyStoryPrivacyState(
|
||||||
|
privacyMode = privacyData.privacyMode,
|
||||||
|
connectionCount = if (privacyData.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) privacyData.rawMemberCount else privacyData.memberCount
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package org.thoughtcrime.securesms.stories.settings.privacy
|
package org.thoughtcrime.securesms.stories.settings.privacy
|
||||||
|
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.WrapperDialogFragment
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||||
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
|
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
|
||||||
|
|
||||||
|
@ -20,6 +23,18 @@ abstract class ChangeMyStoryMembershipFragment : BaseStoryRecipientSelectionFrag
|
||||||
class AllExceptFragment : ChangeMyStoryMembershipFragment() {
|
class AllExceptFragment : ChangeMyStoryMembershipFragment() {
|
||||||
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__all_except
|
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__all_except
|
||||||
override val checkboxResource: Int = R.drawable.contact_selection_exclude_checkbox
|
override val checkboxResource: Int = R.drawable.contact_selection_exclude_checkbox
|
||||||
|
|
||||||
|
class Dialog : WrapperDialogFragment() {
|
||||||
|
override fun getWrappedFragment(): Fragment {
|
||||||
|
return AllExceptFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAsDialog(): DialogFragment {
|
||||||
|
return Dialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,4 +42,16 @@ class AllExceptFragment : ChangeMyStoryMembershipFragment() {
|
||||||
*/
|
*/
|
||||||
class OnlyShareWithFragment : ChangeMyStoryMembershipFragment() {
|
class OnlyShareWithFragment : ChangeMyStoryMembershipFragment() {
|
||||||
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__only_share_with
|
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__only_share_with
|
||||||
|
|
||||||
|
class Dialog : WrapperDialogFragment() {
|
||||||
|
override fun getWrappedFragment(): Fragment {
|
||||||
|
return OnlyShareWithFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAsDialog(): DialogFragment {
|
||||||
|
return Dialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
package org.thoughtcrime.securesms.stories.settings.privacy
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
|
||||||
|
import org.thoughtcrime.securesms.components.WrapperDialogFragment
|
||||||
|
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
|
||||||
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
|
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||||
|
import org.thoughtcrime.securesms.util.visible
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose the initial settings for My Story when first sending to My Story.
|
||||||
|
*/
|
||||||
|
class ChooseInitialMyStoryMembershipBottomSheetDialogFragment :
|
||||||
|
FixedRoundedCornerBottomSheetDialogFragment(),
|
||||||
|
WrapperDialogFragment.WrapperDialogFragmentCallback,
|
||||||
|
BaseStoryRecipientSelectionFragment.Callback {
|
||||||
|
|
||||||
|
private val viewModel: ChooseInitialMyStoryMembershipViewModel by viewModels()
|
||||||
|
|
||||||
|
private lateinit var lifecycleDisposable: LifecycleDisposable
|
||||||
|
|
||||||
|
private lateinit var allRow: View
|
||||||
|
private lateinit var allExceptRow: View
|
||||||
|
private lateinit var onlyWitRow: View
|
||||||
|
|
||||||
|
private lateinit var allRadio: MaterialRadioButton
|
||||||
|
private lateinit var allExceptRadio: MaterialRadioButton
|
||||||
|
private lateinit var onlyWitRadio: MaterialRadioButton
|
||||||
|
|
||||||
|
private lateinit var allExceptCount: TextView
|
||||||
|
private lateinit var onlyWithCount: TextView
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.choose_initial_my_story_membership_fragment, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
allRow = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_row)
|
||||||
|
allExceptRow = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_row)
|
||||||
|
onlyWitRow = view.findViewById(R.id.choose_initial_my_story_only_share_with_row)
|
||||||
|
|
||||||
|
allRadio = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_radio)
|
||||||
|
allExceptRadio = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_radio)
|
||||||
|
onlyWitRadio = view.findViewById(R.id.choose_initial_my_story_only_share_with_radio)
|
||||||
|
|
||||||
|
allExceptCount = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_count)
|
||||||
|
onlyWithCount = view.findViewById(R.id.choose_initial_my_story_only_share_with_count)
|
||||||
|
|
||||||
|
val save = view.findViewById<View>(R.id.choose_initial_my_story_save).apply {
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleDisposable = LifecycleDisposable().apply { bindTo(viewLifecycleOwner) }
|
||||||
|
|
||||||
|
lifecycleDisposable += viewModel.state
|
||||||
|
.subscribe { state ->
|
||||||
|
allRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ALL
|
||||||
|
allExceptRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT
|
||||||
|
onlyWitRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ONLY_WITH
|
||||||
|
|
||||||
|
allExceptCount.visible = allExceptRadio.isChecked
|
||||||
|
onlyWithCount.visible = onlyWitRadio.isChecked
|
||||||
|
|
||||||
|
when (state.privacyState.privacyMode) {
|
||||||
|
DistributionListPrivacyMode.ALL_EXCEPT -> allExceptCount.text = resources.getQuantityString(R.plurals.MyStorySettingsFragment__d_people_excluded, state.privacyState.connectionCount, state.privacyState.connectionCount)
|
||||||
|
DistributionListPrivacyMode.ONLY_WITH -> onlyWithCount.text = resources.getQuantityString(R.plurals.MyStorySettingsFragment__d_people, state.privacyState.connectionCount, state.privacyState.connectionCount)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
save.isEnabled = state.recipientId != null
|
||||||
|
}
|
||||||
|
|
||||||
|
val clickListener = { v: View ->
|
||||||
|
val selection = when (v) {
|
||||||
|
allRow -> DistributionListPrivacyMode.ALL
|
||||||
|
allExceptRow -> DistributionListPrivacyMode.ALL_EXCEPT
|
||||||
|
onlyWitRow -> DistributionListPrivacyMode.ONLY_WITH
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel
|
||||||
|
.select(selection)
|
||||||
|
.subscribe { confirmedSelection ->
|
||||||
|
when (confirmedSelection) {
|
||||||
|
DistributionListPrivacyMode.ALL_EXCEPT -> AllExceptFragment.createAsDialog().show(childFragmentManager, SELECTION_FRAGMENT)
|
||||||
|
DistributionListPrivacyMode.ONLY_WITH -> OnlyShareWithFragment.createAsDialog().show(childFragmentManager, SELECTION_FRAGMENT)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listOf(allRow, allExceptRow, onlyWitRow).forEach { it.setOnClickListener { v -> clickListener(v) } }
|
||||||
|
|
||||||
|
save.setOnClickListener {
|
||||||
|
lifecycleDisposable += viewModel
|
||||||
|
.save()
|
||||||
|
.subscribe { recipientId ->
|
||||||
|
dismissAllowingStateLoss()
|
||||||
|
findListener<Callback>()?.onMyStoryConfigured(recipientId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun exitFlow() {
|
||||||
|
(childFragmentManager.findFragmentByTag(SELECTION_FRAGMENT) as? DialogFragment)?.dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWrapperDialogFragmentDismissed() = Unit
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SELECTION_FRAGMENT = "selection_fragment"
|
||||||
|
|
||||||
|
fun show(fragmentManager: FragmentManager) {
|
||||||
|
val fragment = ChooseInitialMyStoryMembershipBottomSheetDialogFragment()
|
||||||
|
fragment.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onMyStoryConfigured(recipientId: RecipientId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.thoughtcrime.securesms.stories.settings.privacy
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.thoughtcrime.securesms.stories.settings.my.MyStoryPrivacyState
|
||||||
|
|
||||||
|
data class ChooseInitialMyStoryMembershipState(val recipientId: RecipientId? = null, val privacyState: MyStoryPrivacyState = MyStoryPrivacyState())
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.thoughtcrime.securesms.stories.settings.privacy
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.core.Flowable
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||||
|
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsRepository
|
||||||
|
import org.thoughtcrime.securesms.util.rx.RxStore
|
||||||
|
|
||||||
|
class ChooseInitialMyStoryMembershipViewModel @JvmOverloads constructor(
|
||||||
|
private val repository: MyStorySettingsRepository = MyStorySettingsRepository()
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val store = RxStore(ChooseInitialMyStoryMembershipState())
|
||||||
|
private val disposables = CompositeDisposable()
|
||||||
|
|
||||||
|
val state: Flowable<ChooseInitialMyStoryMembershipState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|
||||||
|
init {
|
||||||
|
disposables += repository.observeChooseInitialPrivacy()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.subscribe { state -> store.update { state } }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
disposables.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun select(selection: DistributionListPrivacyMode): Single<DistributionListPrivacyMode> {
|
||||||
|
return repository.setPrivacyMode(selection)
|
||||||
|
.toSingleDefault(selection)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(): Single<RecipientId> {
|
||||||
|
return Single.fromCallable<RecipientId> {
|
||||||
|
SignalStore.storyValues().userHasBeenNotifiedAboutStories = true
|
||||||
|
store.state.recipientId
|
||||||
|
}.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.stories.settings.privacy
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import org.thoughtcrime.securesms.R
|
|
||||||
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Embeds HideStoryFromFragment in a full-screen dialog.
|
|
||||||
*/
|
|
||||||
class HideStoryFromDialogFragment : DialogFragment(R.layout.fragment_container), BaseStoryRecipientSelectionFragment.Callback {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
// TODO [stories] replace with new bottom sheet
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun exitFlow() {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="48dp">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/anchor"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@color/signal_icon_tint_tab_unselected" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/choose_initial_my_story_title"
|
||||||
|
style="@style/Signal.Text.TitleLarge"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="@string/ChooseInitialMyStoryMembershipFragment__my_story_privacy" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Signal.Text.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="@string/ChooseInitialMyStoryMembershipFragment__choose_who_can_see_posts_to_my_story_you_can_always_make_changes_in_settings"
|
||||||
|
android:textColor="@color/signal_colorOnSurfaceVariant" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_radio"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Signal.Text.BodyLarge"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="@string/ChooseInitialMyStoryMembershipFragment__all_signal_connections" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_except_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_except_radio"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_except_message"
|
||||||
|
style="@style/Signal.Text.BodyLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="@string/ChooseInitialMyStoryMembershipFragment__all_signal_connections_except"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/choose_initial_my_story_all_signal_connnections_except_count"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/choose_initial_my_story_all_signal_connnections_except_radio"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/choose_initial_my_story_all_signal_connnections_except_count"
|
||||||
|
style="@style/Signal.Text.BodyMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/choose_initial_my_story_all_signal_connnections_except_message"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/choose_initial_my_story_all_signal_connnections_except_message"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/choose_initial_my_story_all_signal_connnections_except_message"
|
||||||
|
tools:text="Asdf"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/choose_initial_my_story_only_share_with_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
|
android:id="@+id/choose_initial_my_story_only_share_with_radio"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/choose_initial_my_story_only_share_with_message"
|
||||||
|
style="@style/Signal.Text.BodyLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="@string/ChooseInitialMyStoryMembershipFragment__only_share_with"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/choose_initial_my_story_only_share_with_count"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/choose_initial_my_story_only_share_with_radio"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/choose_initial_my_story_only_share_with_count"
|
||||||
|
style="@style/Signal.Text.BodyMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/choose_initial_my_story_only_share_with_message"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/choose_initial_my_story_only_share_with_message"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/choose_initial_my_story_only_share_with_message"
|
||||||
|
tools:text="Asdf"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/choose_initial_my_story_save"
|
||||||
|
style="@style/Signal.Widget.Button.Medium.Tonal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:text="@string/save" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</ScrollView>
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/action_button"
|
android:id="@+id/action_button"
|
||||||
style="@style/Signal.Widget.Button.Large.Primary"
|
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
|
|
|
@ -4888,6 +4888,8 @@
|
||||||
<item quantity="one">Group Story · %1$d viewer</item>
|
<item quantity="one">Group Story · %1$d viewer</item>
|
||||||
<item quantity="other">Group Story · %1$d viewers</item>
|
<item quantity="other">Group Story · %1$d viewers</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<!-- Label under name for My Story when first sending to my story -->
|
||||||
|
<string name="ContactSearchItems__tap_to_choose_your_viewers">Tap to choose your viewers</string>
|
||||||
<!-- Label for context menu item to open story settings -->
|
<!-- Label for context menu item to open story settings -->
|
||||||
<string name="ContactSearchItems__story_settings">Story settings</string>
|
<string name="ContactSearchItems__story_settings">Story settings</string>
|
||||||
<!-- Label for context menu item to remove a group story from contact results -->
|
<!-- Label for context menu item to remove a group story from contact results -->
|
||||||
|
@ -4933,6 +4935,17 @@
|
||||||
<!-- Button label to confirm understanding of story navigation -->
|
<!-- Button label to confirm understanding of story navigation -->
|
||||||
<string name="StoryFirstTimeNagivationView__got_it">Got it</string>
|
<string name="StoryFirstTimeNagivationView__got_it">Got it</string>
|
||||||
|
|
||||||
|
<!-- Title of initial My Story settings configuration shown when sending to My Story for the first time -->
|
||||||
|
<string name="ChooseInitialMyStoryMembershipFragment__my_story_privacy">My Story Privacy</string>
|
||||||
|
<!-- Subtitle of initial My Story settings configuration shown when sending to My Story for the first time -->
|
||||||
|
<string name="ChooseInitialMyStoryMembershipFragment__choose_who_can_see_posts_to_my_story_you_can_always_make_changes_in_settings">Choose who can see posts to My Story. You can always make changes in settings.</string>
|
||||||
|
<!-- All connections option for initial My Story settings configuration shown when sending to My Story for the first time -->
|
||||||
|
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections">All Signal connections</string>
|
||||||
|
<!-- All connections except option for initial My Story settings configuration shown when sending to My Story for the first time -->
|
||||||
|
<string name="ChooseInitialMyStoryMembershipFragment__all_signal_connections_except">All Signal connections except…</string>
|
||||||
|
<!-- Only with selected connections option for initial My Story settings configuration shown when sending to My Story for the first time -->
|
||||||
|
<string name="ChooseInitialMyStoryMembershipFragment__only_share_with">Only share with…</string>
|
||||||
|
|
||||||
<!-- EOF -->
|
<!-- EOF -->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Ładowanie…
Reference in New Issue