diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java index 42a6f2c930..f16c9a068c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java @@ -11,6 +11,7 @@ import android.util.AttributeSet; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; +import org.signal.core.util.BreakIteratorCompat; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView; @@ -19,6 +20,7 @@ import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.ViewUtil; +import java.util.Iterator; import java.util.Objects; public class FromTextView extends SimpleEmojiTextView { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt index 1061b55e39..962b6f6d44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt @@ -52,7 +52,8 @@ class ContactSearchConfiguration private constructor( val includeSelf: Boolean, val transportType: TransportType, override val includeHeader: Boolean, - override val expandConfig: ExpandConfig? = null + override val expandConfig: ExpandConfig? = null, + val includeLetterHeaders: Boolean = false ) : Section(SectionKey.INDIVIDUALS) /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchData.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchData.kt index 6ec9937934..f7ba55ba20 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchData.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchData.kt @@ -23,7 +23,11 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) { /** * A row displaying a known recipient. */ - data class KnownRecipient(val recipient: Recipient, val shortSummary: Boolean = false) : ContactSearchData(ContactSearchKey.RecipientSearchKey.KnownRecipient(recipient.id)) + data class KnownRecipient( + val recipient: Recipient, + val shortSummary: Boolean = false, + val headerLetter: String? = null + ) : ContactSearchData(ContactSearchKey.RecipientSearchKey.KnownRecipient(recipient.id)) /** * A row containing a title for a given section diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt index bc640c45ca..c575d72d82 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView import org.thoughtcrime.securesms.components.FromTextView import org.thoughtcrime.securesms.components.menu.ActionItem import org.thoughtcrime.securesms.components.menu.SignalContextMenu +import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient @@ -226,7 +227,10 @@ object ContactSearchItems { } } - private class KnownRecipientViewHolder(itemView: View, displayCheckBox: Boolean, onClick: RecipientClickListener) : BaseRecipientViewHolder(itemView, displayCheckBox, onClick) { + private class KnownRecipientViewHolder(itemView: View, displayCheckBox: Boolean, onClick: RecipientClickListener) : BaseRecipientViewHolder(itemView, displayCheckBox, onClick), LetterHeaderDecoration.LetterHeaderItem { + + private var headerLetter: String? = null + override fun isSelected(model: RecipientModel): Boolean = model.isSelected override fun getData(model: RecipientModel): ContactSearchData.KnownRecipient = model.knownRecipient override fun getRecipient(model: RecipientModel): Recipient = model.knownRecipient.recipient @@ -235,10 +239,16 @@ object ContactSearchItems { if (model.shortSummary && recipient.isGroup) { val count = recipient.participantIds.size - number.setText(context.resources.getQuantityString(R.plurals.ContactSearchItems__group_d_members, count, count)) + number.text = context.resources.getQuantityString(R.plurals.ContactSearchItems__group_d_members, count, count) } else { super.bindNumberField(model) } + + headerLetter = model.knownRecipient.headerLetter + } + + override fun getHeaderLetter(): String? { + return headerLetter } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt index fc2e9071d4..580cc12d02 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.keyvalue.StorySend import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId import java.util.concurrent.TimeUnit /** @@ -129,6 +130,13 @@ class ContactSearchPagedDataSource( } } + private fun getNonGroupHeaderLetterMap(section: ContactSearchConfiguration.Section.Individuals, query: String?): Map { + return when (section.transportType) { + ContactSearchConfiguration.TransportType.PUSH -> contactSearchPagedDataSourceRepository.querySignalContactLetterHeaders(query, section.includeSelf) + else -> error("This has only been implemented for push recipients.") + } + } + private fun getStoriesSearchIterator(query: String?): ContactSearchIterator { return CursorSearchIterator(contactSearchPagedDataSourceRepository.getStories(query)) } @@ -193,6 +201,12 @@ class ContactSearchPagedDataSource( } private fun getNonGroupContactsData(section: ContactSearchConfiguration.Section.Individuals, query: String?, startIndex: Int, endIndex: Int): List { + val headerMap: Map = if (section.includeLetterHeaders) { + getNonGroupHeaderLetterMap(section, query) + } else { + emptyMap() + } + return getNonGroupSearchIterator(section, query).use { records -> readContactData( records = records, @@ -201,7 +215,8 @@ class ContactSearchPagedDataSource( startIndex = startIndex, endIndex = endIndex, recordMapper = { - ContactSearchData.KnownRecipient(contactSearchPagedDataSourceRepository.getRecipientFromRecipientCursor(it)) + val recipient = contactSearchPagedDataSourceRepository.getRecipientFromRecipientCursor(it) + ContactSearchData.KnownRecipient(recipient, headerLetter = headerMap[recipient.id]) } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt index e5552ffa54..2f9439d6c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt @@ -36,6 +36,10 @@ open class ContactSearchPagedDataSourceRepository( return contactRepository.querySignalContacts(query ?: "", includeSelf) } + open fun querySignalContactLetterHeaders(query: String?, includeSelf: Boolean): Map { + return SignalDatabase.recipients.querySignalContactLetterHeaders(query ?: "", includeSelf) + } + open fun queryNonSignalContacts(query: String?): Cursor? { return contactRepository.queryNonSignalContacts(query ?: "") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.kt index 76d314f434..90d4eca404 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.kt @@ -3112,6 +3112,44 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) : return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy) } + fun querySignalContactLetterHeaders(inputQuery: String, includeSelf: Boolean): Map { + val searchSelection = ContactSearchSelection.Builder() + .withRegistered(true) + .withGroups(false) + .excludeId(if (includeSelf) null else Recipient.self().id) + .withSearchQuery(inputQuery) + .build() + + return readableDatabase.query( + """ + SELECT + _id, + UPPER(SUBSTR($SORT_NAME, 0, 2)) AS letter_header + FROM ( + SELECT ${SEARCH_PROJECTION.joinToString(", ")} + FROM recipient + WHERE ${searchSelection.where} + ORDER BY $SORT_NAME, $SYSTEM_JOINED_NAME, $SEARCH_PROFILE_NAME, $PHONE + ) + GROUP BY letter_header + """.trimIndent(), + searchSelection.args + ).use { cursor -> + if (cursor.count == 0) { + emptyMap() + } else { + val resultsMap = mutableMapOf() + while (cursor.moveToNext()) { + cursor.requireString("letter_header")?.let { + resultsMap[RecipientId.from(cursor.requireLong(ID))] = it + } + } + + resultsMap + } + } + } + fun getNonSignalContacts(): Cursor? { val searchSelection = ContactSearchSelection.Builder().withNonRegistered(true) .withGroups(false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt new file mode 100644 index 0000000000..e8979c68a5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt @@ -0,0 +1,60 @@ +package org.thoughtcrime.securesms.stories.settings.connections + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.ViewBinderDelegate +import org.thoughtcrime.securesms.components.WrapperDialogFragment +import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration +import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration +import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator +import org.thoughtcrime.securesms.databinding.ViewAllSignalConnectionsFragmentBinding +import org.thoughtcrime.securesms.groups.SelectionLimits + +class ViewAllSignalConnectionsFragment : Fragment(R.layout.view_all_signal_connections_fragment) { + + private val binding by ViewBinderDelegate(ViewAllSignalConnectionsFragmentBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.recycler.addItemDecoration(LetterHeaderDecoration(requireContext()) { false }) + binding.toolbar.setNavigationOnClickListener { + requireActivity().onBackPressedDispatcher.onBackPressed() + } + + ContactSearchMediator( + fragment = this, + recyclerView = binding.recycler, + selectionLimits = SelectionLimits(0, 0), + displayCheckBox = false, + mapStateToConfiguration = { getConfiguration() }, + performSafetyNumberChecks = false + ) + } + + private fun getConfiguration(): ContactSearchConfiguration { + return ContactSearchConfiguration.build { + addSection( + ContactSearchConfiguration.Section.Individuals( + includeHeader = false, + includeSelf = false, + includeLetterHeaders = true, + transportType = ContactSearchConfiguration.TransportType.PUSH + ) + ) + } + } + + class Dialog : WrapperDialogFragment() { + override fun getWrappedFragment(): Fragment { + return ViewAllSignalConnectionsFragment() + } + + companion object { + fun show(fragmentManager: FragmentManager) { + Dialog().show(fragmentManager, null) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/AllSignalConnectionsRowItem.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/AllSignalConnectionsRowItem.kt new file mode 100644 index 0000000000..3e89bcb97a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/AllSignalConnectionsRowItem.kt @@ -0,0 +1,70 @@ +package org.thoughtcrime.securesms.stories.settings.my + +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.databinding.AllSignalConnectionsRowItemBinding +import org.thoughtcrime.securesms.util.adapter.mapping.BindingFactory +import org.thoughtcrime.securesms.util.adapter.mapping.BindingViewHolder +import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter +import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel +import org.thoughtcrime.securesms.util.visible + +/** + * AllSignalConnections privacy setting row item with "View" support + */ +object AllSignalConnectionsRowItem { + + private const val IS_CHECKED = 0 + private const val IS_COUNT = 1 + + fun register(mappingAdapter: MappingAdapter) { + mappingAdapter.registerFactory(Model::class.java, BindingFactory(::ViewHolder, AllSignalConnectionsRowItemBinding::inflate)) + } + + class Model( + val isChecked: Boolean, + val count: Int, + val onRowClicked: () -> Unit, + val onViewClicked: () -> Unit + ) : MappingModel { + + override fun areItemsTheSame(newItem: Model): Boolean = true + + override fun areContentsTheSame(newItem: Model): Boolean = isChecked == newItem.isChecked && count == newItem.count + + override fun getChangePayload(newItem: Model): Any? { + val isCheckedDifferent = isChecked != newItem.isChecked + val isCountDifferent = count != newItem.count + + return when { + isCheckedDifferent && !isCountDifferent -> IS_CHECKED + !isCheckedDifferent && isCountDifferent -> IS_COUNT + else -> null + } + } + } + + private class ViewHolder(binding: AllSignalConnectionsRowItemBinding) : BindingViewHolder(binding) { + override fun bind(model: Model) { + binding.root.setOnClickListener { model.onRowClicked() } + binding.view.setOnClickListener { model.onViewClicked() } + + when { + payload.contains(IS_COUNT) -> presentCount(model.count) + payload.contains(IS_CHECKED) -> presentSelected(model.isChecked) + else -> { + presentCount(model.count) + presentSelected(model.isChecked) + } + } + } + + private fun presentCount(count: Int) { + binding.count.visible = count > 0 + binding.count.text = context.resources.getQuantityString(R.plurals.MyStorySettingsFragment__viewers, count, count) + } + + private fun presentSelected(isChecked: Boolean) { + binding.radio.isChecked = isChecked + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt index 984b878b4b..d741d1e749 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode +import org.thoughtcrime.securesms.stories.settings.connections.ViewAllSignalConnectionsFragment import org.thoughtcrime.securesms.util.LifecycleDisposable import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -38,6 +39,7 @@ class MyStorySettingsFragment : DSLSettingsFragment( } override fun bindAdapter(adapter: MappingAdapter) { + AllSignalConnectionsRowItem.register(adapter) viewModel.state.observe(viewLifecycleOwner) { state -> adapter.submitList(getConfiguration(state).toMappingModelList()) } @@ -47,14 +49,18 @@ class MyStorySettingsFragment : DSLSettingsFragment( return configure { sectionHeaderPref(R.string.MyStorySettingsFragment__who_can_view_this_story) - radioPref( - title = DSLSettingsText.from(R.string.MyStorySettingsFragment__all_signal_connections), - summary = DSLSettingsText.from(R.string.MyStorySettingsFragment__share_with_all_connections), - isChecked = state.myStoryPrivacyState.privacyMode == DistributionListPrivacyMode.ALL, - onClick = { - lifecycleDisposable += viewModel.setMyStoryPrivacyMode(DistributionListPrivacyMode.ALL) - .subscribe() - } + customPref( + AllSignalConnectionsRowItem.Model( + isChecked = state.myStoryPrivacyState.privacyMode == DistributionListPrivacyMode.ALL, + count = state.allSignalConnectionsCount, + onRowClicked = { + lifecycleDisposable += viewModel.setMyStoryPrivacyMode(DistributionListPrivacyMode.ALL) + .subscribe() + }, + onViewClicked = { + ViewAllSignalConnectionsFragment.Dialog.show(parentFragmentManager) + } + ) ) val exceptText = if (state.myStoryPrivacyState.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt index 9cc63d3f0e..714f79496a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt @@ -22,11 +22,15 @@ class MyStorySettingsRepository { } fun observeChooseInitialPrivacy(): Observable { - return Single.fromCallable { SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!! } + return Single + .fromCallable { SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!! } .subscribeOn(Schedulers.io()) .flatMapObservable { recipientId -> - Recipient.observable(recipientId) + val allSignalConnectionsCount = getAllSignalConnectionsCount().toObservable() + val stateWithoutCount = Recipient.observable(recipientId) .flatMap { Observable.just(ChooseInitialMyStoryMembershipState(recipientId = recipientId, privacyState = getStoryPrivacyState())) } + + Observable.combineLatest(allSignalConnectionsCount, stateWithoutCount) { count, state -> state.copy(allSignalConnectionsCount = count) } } } @@ -50,6 +54,12 @@ class MyStorySettingsRepository { }.subscribeOn(Schedulers.io()) } + fun getAllSignalConnectionsCount(): Single { + return Single.fromCallable { + SignalDatabase.recipients.getSignalContactsCount(false) + }.subscribeOn(Schedulers.io()) + } + @WorkerThread private fun getStoryPrivacyState(): MyStoryPrivacyState { val privacyData: DistributionListPrivacyData = SignalDatabase.distributionLists.getPrivacyData(DistributionListId.MY_STORY) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsState.kt index 1640e4212c..ef63a19cc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsState.kt @@ -2,5 +2,6 @@ package org.thoughtcrime.securesms.stories.settings.my data class MyStorySettingsState( val myStoryPrivacyState: MyStoryPrivacyState = MyStoryPrivacyState(), - val areRepliesAndReactionsEnabled: Boolean = false + val areRepliesAndReactionsEnabled: Boolean = false, + val allSignalConnectionsCount: Int = 0 ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsViewModel.kt index cb6060d269..0cbf79d4ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsViewModel.kt @@ -25,6 +25,8 @@ class MyStorySettingsViewModel @JvmOverloads constructor(private val repository: .subscribe { myStoryPrivacyState -> store.update { it.copy(myStoryPrivacyState = myStoryPrivacyState) } } disposables += repository.getRepliesAndReactionsEnabled() .subscribe { repliesAndReactionsEnabled -> store.update { it.copy(areRepliesAndReactionsEnabled = repliesAndReactionsEnabled) } } + disposables += repository.getAllSignalConnectionsCount() + .subscribe { allSignalConnectionsCount -> store.update { it.copy(allSignalConnectionsCount = allSignalConnectionsCount) } } } fun setRepliesAndReactionsEnabled(repliesAndReactionsEnabled: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipBottomSheetDialogFragment.kt index d61caef1ad..a0163fdd97 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipBottomSheetDialogFragment.kt @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialog 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.connections.ViewAllSignalConnectionsFragment import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment import org.thoughtcrime.securesms.util.BottomSheetUtil import org.thoughtcrime.securesms.util.LifecycleDisposable @@ -42,9 +43,12 @@ class ChooseInitialMyStoryMembershipBottomSheetDialogFragment : private lateinit var allExceptRadio: MaterialRadioButton private lateinit var onlyWitRadio: MaterialRadioButton + private lateinit var allCount: TextView private lateinit var allExceptCount: TextView private lateinit var onlyWithCount: TextView + private lateinit var allView: View + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.choose_initial_my_story_membership_fragment, container, false) } @@ -58,9 +62,15 @@ class ChooseInitialMyStoryMembershipBottomSheetDialogFragment : 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) + allCount = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_count) 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) + allView = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_view) + allView.setOnClickListener { + ViewAllSignalConnectionsFragment.Dialog.show(parentFragmentManager) + } + val save = view.findViewById(R.id.choose_initial_my_story_save).apply { isEnabled = false } @@ -76,6 +86,9 @@ class ChooseInitialMyStoryMembershipBottomSheetDialogFragment : allExceptCount.visible = allExceptRadio.isChecked onlyWithCount.visible = onlyWitRadio.isChecked + allCount.visible = state.allSignalConnectionsCount > 0 + allCount.text = resources.getQuantityString(R.plurals.MyStorySettingsFragment__viewers, state.allSignalConnectionsCount, state.allSignalConnectionsCount) + 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) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipState.kt index d284a8a103..14ae833224 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/privacy/ChooseInitialMyStoryMembershipState.kt @@ -3,4 +3,8 @@ 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()) +data class ChooseInitialMyStoryMembershipState( + val recipientId: RecipientId? = null, + val privacyState: MyStoryPrivacyState = MyStoryPrivacyState(), + val allSignalConnectionsCount: Int = 0 +) diff --git a/app/src/main/res/layout/all_signal_connections_row_item.xml b/app/src/main/res/layout/all_signal_connections_row_item.xml new file mode 100644 index 0000000000..dbb652ff9b --- /dev/null +++ b/app/src/main/res/layout/all_signal_connections_row_item.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/choose_initial_my_story_membership_fragment.xml b/app/src/main/res/layout/choose_initial_my_story_membership_fragment.xml index 6885439424..ce2b826e01 100644 --- a/app/src/main/res/layout/choose_initial_my_story_membership_fragment.xml +++ b/app/src/main/res/layout/choose_initial_my_story_membership_fragment.xml @@ -1,10 +1,10 @@ + android:layout_height="wrap_content" + tools:viewBindingIgnore="true"> - + android:paddingStart="@dimen/dsl_settings_gutter"> + android:clickable="false" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:text="@string/ChooseInitialMyStoryMembershipFragment__all_signal_connections" + app:layout_constrainedWidth="true" + app:layout_constraintBottom_toTopOf="@+id/choose_initial_my_story_all_signal_connnections_count" + app:layout_constraintEnd_toStartOf="@id/choose_initial_my_story_all_signal_connnections_view" + app:layout_constraintStart_toEndOf="@id/choose_initial_my_story_all_signal_connnections_radio" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> - + + + + + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94d567e9a9..cde8c9f325 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4849,6 +4849,13 @@ Delete My Story + + + %1$d viewer + %1$d viewers + + + View Who can view this story diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c51ef60fdb..85b7d58ffb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,6 +17,11 @@ + +