mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 21:15:48 +00:00
Add Banners to all reminder usages behind remote config.
This commit is contained in:
committed by
mtang-signal
parent
f296fcd716
commit
e2e6a73e8d
@@ -46,8 +46,8 @@ class CdsPermanentErrorBanner(private val fragmentManager: FragmentManager) : Ba
|
||||
val PERMANENT_TIME_CUTOFF = 30.days.inWholeMilliseconds
|
||||
|
||||
@JvmStatic
|
||||
fun createFlow(fragmentManager: FragmentManager): Flow<CdsPermanentErrorBanner> = createAndEmit {
|
||||
CdsPermanentErrorBanner(fragmentManager)
|
||||
fun createFlow(childFragmentManager: FragmentManager): Flow<CdsPermanentErrorBanner> = createAndEmit {
|
||||
CdsPermanentErrorBanner(childFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class CdsTemporaryErrorBanner(private val fragmentManager: FragmentManager) : Ba
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun createFlow(fragmentManager: FragmentManager): Flow<CdsTemporaryErrorBanner> = createAndEmit {
|
||||
CdsTemporaryErrorBanner(fragmentManager)
|
||||
fun createFlow(childFragmentManager: FragmentManager): Flow<CdsTemporaryErrorBanner> = createAndEmit {
|
||||
CdsTemporaryErrorBanner(childFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.thoughtcrime.securesms.banner.banners
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -20,12 +19,15 @@ import org.thoughtcrime.securesms.util.PowerManagerCompat
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
@RequiresApi(23)
|
||||
class DozeBanner(private val context: Context) : Banner() {
|
||||
override val enabled: Boolean = !SignalStore.account.fcmEnabled && !TextSecurePreferences.hasPromptedOptimizeDoze(context) && Build.VERSION.SDK_INT >= 23 && !ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.packageName)
|
||||
override val enabled: Boolean =
|
||||
Build.VERSION.SDK_INT >= 23 && !SignalStore.account.fcmEnabled && !TextSecurePreferences.hasPromptedOptimizeDoze(context) && !ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.packageName)
|
||||
|
||||
@Composable
|
||||
override fun DisplayBanner() {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
throw IllegalStateException("Showing a Doze banner for an OS prior to Android 6.0")
|
||||
}
|
||||
DefaultBanner(
|
||||
title = stringResource(id = R.string.DozeReminder_optimize_for_missing_play_services),
|
||||
body = stringResource(id = R.string.DozeReminder_this_device_does_not_support_play_services_tap_to_disable_system_battery),
|
||||
@@ -45,11 +47,7 @@ class DozeBanner(private val context: Context) : Banner() {
|
||||
|
||||
@JvmStatic
|
||||
fun createFlow(context: Context): Flow<DozeBanner> = createAndEmit {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
DozeBanner(context)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
DozeBanner(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -40,7 +40,9 @@ class MediaRestoreProgressBanner(private val data: MediaRestoreEvent) : Banner()
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
return observer.flow
|
||||
} else {
|
||||
return emptyFlow()
|
||||
return flow {
|
||||
emit(MediaRestoreProgressBanner(MediaRestoreEvent(0L, 0L)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,22 +22,38 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
* Banner to let the user know their build is about to expire or has expired.
|
||||
*
|
||||
* @param status can be used to filter which conditions are shown.
|
||||
*/
|
||||
class OutdatedBuildBanner(val context: Context, private val daysUntilExpiry: Int) : Banner() {
|
||||
class OutdatedBuildBanner(val context: Context, private val daysUntilExpiry: Int, private val status: ExpiryStatus) : Banner() {
|
||||
|
||||
override val enabled = SignalStore.misc.isClientDeprecated || daysUntilExpiry <= MAX_DAYS_UNTIL_EXPIRE
|
||||
override val enabled = when (status) {
|
||||
ExpiryStatus.OUTDATED_ONLY -> SignalStore.misc.isClientDeprecated
|
||||
ExpiryStatus.EXPIRED_ONLY -> daysUntilExpiry <= MAX_DAYS_UNTIL_EXPIRE
|
||||
ExpiryStatus.OUTDATED_OR_EXPIRED -> SignalStore.misc.isClientDeprecated || daysUntilExpiry <= MAX_DAYS_UNTIL_EXPIRE
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun DisplayBanner() {
|
||||
DefaultBanner(
|
||||
title = null,
|
||||
body = if (SignalStore.misc.isClientDeprecated) {
|
||||
val bodyText = when (status) {
|
||||
ExpiryStatus.OUTDATED_ONLY -> if (daysUntilExpiry == 0) {
|
||||
stringResource(id = R.string.OutdatedBuildReminder_your_version_of_signal_will_expire_today)
|
||||
} else {
|
||||
pluralStringResource(id = R.plurals.OutdatedBuildReminder_your_version_of_signal_will_expire_in_n_days, count = daysUntilExpiry, daysUntilExpiry)
|
||||
}
|
||||
|
||||
ExpiryStatus.EXPIRED_ONLY -> stringResource(id = R.string.OutdatedBuildReminder_your_version_of_signal_will_expire_today)
|
||||
ExpiryStatus.OUTDATED_OR_EXPIRED -> if (SignalStore.misc.isClientDeprecated) {
|
||||
stringResource(id = R.string.OutdatedBuildReminder_your_version_of_signal_will_expire_today)
|
||||
} else if (daysUntilExpiry == 0) {
|
||||
stringResource(id = R.string.OutdatedBuildReminder_your_version_of_signal_will_expire_today)
|
||||
} else {
|
||||
pluralStringResource(id = R.plurals.OutdatedBuildReminder_your_version_of_signal_will_expire_in_n_days, count = daysUntilExpiry, daysUntilExpiry)
|
||||
},
|
||||
}
|
||||
}
|
||||
DefaultBanner(
|
||||
title = null,
|
||||
body = bodyText,
|
||||
importance = if (SignalStore.misc.isClientDeprecated) {
|
||||
Importance.ERROR
|
||||
} else {
|
||||
@@ -51,13 +67,25 @@ class OutdatedBuildBanner(val context: Context, private val daysUntilExpiry: Int
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A enumeration for [OutdatedBuildBanner] to limit it to showing either [OUTDATED_ONLY] status, [EXPIRED_ONLY] status, or both.
|
||||
*
|
||||
* [OUTDATED_ONLY] refers to builds that are still valid but need to be updated.
|
||||
* [EXPIRED_ONLY] refers to builds that are no longer allowed to connect to the service.
|
||||
*/
|
||||
enum class ExpiryStatus {
|
||||
OUTDATED_ONLY,
|
||||
EXPIRED_ONLY,
|
||||
OUTDATED_OR_EXPIRED
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_DAYS_UNTIL_EXPIRE = 10
|
||||
|
||||
@JvmStatic
|
||||
fun createFlow(context: Context): Flow<OutdatedBuildBanner> = createAndEmit {
|
||||
fun createFlow(context: Context, status: ExpiryStatus): Flow<OutdatedBuildBanner> = createAndEmit {
|
||||
val daysUntilExpiry = Util.getTimeUntilBuildExpiry(SignalStore.misc.estimatedServerTime).milliseconds.inWholeDays.toInt()
|
||||
OutdatedBuildBanner(context, daysUntilExpiry)
|
||||
OutdatedBuildBanner(context, daysUntilExpiry, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.banner.Banner
|
||||
import org.thoughtcrime.securesms.banner.ui.compose.Action
|
||||
@@ -33,14 +32,8 @@ class PendingGroupJoinRequestsBanner(override val enabled: Boolean, private val
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun createFlow(suggestionsSize: Int, onViewClicked: () -> Unit): Flow<PendingGroupJoinRequestsBanner> = Producer(suggestionsSize, onViewClicked).flow
|
||||
}
|
||||
|
||||
private class Producer(suggestionsSize: Int, onViewClicked: () -> Unit) {
|
||||
val dismissListener: () -> Unit = {
|
||||
class Producer(suggestionsSize: Int, onViewClicked: () -> Unit) {
|
||||
private val dismissListener: () -> Unit = {
|
||||
mutableStateFlow.tryEmit(PendingGroupJoinRequestsBanner(false, suggestionsSize, onViewClicked, null))
|
||||
}
|
||||
private val mutableStateFlow: MutableStateFlow<PendingGroupJoinRequestsBanner> = MutableStateFlow(PendingGroupJoinRequestsBanner(true, suggestionsSize, onViewClicked, dismissListener))
|
||||
|
||||
@@ -46,6 +46,9 @@ class UsernameOutOfSyncBanner(private val context: Context, private val username
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* @param onActionClick input is true if both the username and the link are corrupted, false if only the link is corrupted
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createFlow(context: Context, onActionClick: (Boolean) -> Unit): Flow<UsernameOutOfSyncBanner> = createAndEmit {
|
||||
UsernameOutOfSyncBanner(context, SignalStore.account.usernameSyncState, onActionClick)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
@@ -18,6 +19,9 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
import org.signal.core.util.isNotNullOrBlank
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.banner.BannerManager
|
||||
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
|
||||
@@ -59,12 +63,14 @@ class AppSettingsFragment : DSLSettingsFragment(
|
||||
private val viewModel: AppSettingsViewModel by viewModels()
|
||||
|
||||
private lateinit var reminderView: Stub<ReminderView>
|
||||
private lateinit var bannerView: Stub<ComposeView>
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
viewLifecycleOwner.lifecycle.addObserver(InAppPaymentsBottomSheetDelegate(childFragmentManager, viewLifecycleOwner))
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
reminderView = ViewUtil.findStubById(view, R.id.reminder_stub)
|
||||
bannerView = ViewUtil.findStubById(view, R.id.banner_stub)
|
||||
|
||||
updateReminders()
|
||||
}
|
||||
@@ -85,12 +91,21 @@ class AppSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
private fun updateReminders() {
|
||||
if (ExpiredBuildReminder.isEligible()) {
|
||||
showReminder(ExpiredBuildReminder(context))
|
||||
} else if (UnauthorizedReminder.isEligible(context)) {
|
||||
showReminder(UnauthorizedReminder())
|
||||
if (RemoteConfig.newBannerUi) {
|
||||
val bannerFlows = listOf(
|
||||
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
|
||||
UnauthorizedBanner.createFlow(requireContext())
|
||||
)
|
||||
val bannerManager = BannerManager(bannerFlows)
|
||||
bannerManager.setContent(bannerView.get())
|
||||
} else {
|
||||
hideReminders()
|
||||
if (ExpiredBuildReminder.isEligible()) {
|
||||
showReminder(ExpiredBuildReminder(context))
|
||||
} else if (UnauthorizedReminder.isEligible(context)) {
|
||||
showReminder(UnauthorizedReminder())
|
||||
} else {
|
||||
hideReminders()
|
||||
}
|
||||
}
|
||||
viewModel.refreshDeprecatedOrUnregistered()
|
||||
}
|
||||
|
||||
@@ -14,8 +14,12 @@ import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.LinearLayoutCompat
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.transition.addListener
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.banner.Banner
|
||||
import org.thoughtcrime.securesms.banner.BannerManager
|
||||
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.components.reminder.ReminderView
|
||||
@@ -47,6 +51,7 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
) : LinearLayoutCompat(context, attrs, defStyleAttr) {
|
||||
private val unverifiedBannerStub: Stub<UnverifiedBannerView> by lazy { ViewUtil.findStubById(this, R.id.unverified_banner_stub) }
|
||||
private val reminderStub: Stub<ReminderView> by lazy { ViewUtil.findStubById(this, R.id.reminder_stub) }
|
||||
private val bannerStub: Stub<ComposeView> by lazy { ViewUtil.findStubById(this, R.id.banner_stub) }
|
||||
private val reviewBannerStub: Stub<ReviewBannerView> by lazy { ViewUtil.findStubById(this, R.id.review_banner_stub) }
|
||||
private val voiceNotePlayerStub: Stub<View> by lazy { ViewUtil.findStubById(this, R.id.voice_note_player_stub) }
|
||||
|
||||
@@ -56,6 +61,13 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
fun collectAndShowBanners(flows: Iterable<Flow<Banner>>) {
|
||||
val bannerManager = BannerManager(flows)
|
||||
show(stub = bannerStub) {
|
||||
bannerManager.setContent(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun showReminder(reminder: Reminder) {
|
||||
show(
|
||||
stub = reminderStub
|
||||
|
||||
@@ -228,6 +228,7 @@ import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInfoBotto
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult
|
||||
import org.thoughtcrime.securesms.invites.InviteActions
|
||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
|
||||
@@ -1016,17 +1017,38 @@ class ConversationFragment :
|
||||
VoiceMessageRecordingSessionCallbacks()
|
||||
)
|
||||
|
||||
binding.conversationBanner.listener = ConversationBannerListener()
|
||||
viewModel
|
||||
.reminder
|
||||
.subscribeBy { reminder ->
|
||||
if (reminder.isPresent) {
|
||||
binding.conversationBanner.showReminder(reminder.get())
|
||||
} else {
|
||||
binding.conversationBanner.clearReminder()
|
||||
}
|
||||
val conversationBannerListener = ConversationBannerListener()
|
||||
binding.conversationBanner.listener = conversationBannerListener
|
||||
if (RemoteConfig.newBannerUi) {
|
||||
val bannerFlows = viewModel.getBannerFlows(
|
||||
context = requireContext(),
|
||||
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
|
||||
onAddMembers = {
|
||||
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
|
||||
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
|
||||
}
|
||||
},
|
||||
onNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
|
||||
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
|
||||
)
|
||||
|
||||
binding.conversationBanner.collectAndShowBanners(bannerFlows)
|
||||
|
||||
if (TextSecurePreferences.getServiceOutage(context)) {
|
||||
AppDependencies.jobManager.add(ServiceOutageDetectionJob())
|
||||
}
|
||||
.addTo(disposables)
|
||||
} else {
|
||||
viewModel
|
||||
.reminder
|
||||
.subscribeBy { reminder ->
|
||||
if (reminder.isPresent) {
|
||||
binding.conversationBanner.showReminder(reminder.get())
|
||||
} else {
|
||||
binding.conversationBanner.clearReminder()
|
||||
}
|
||||
}
|
||||
.addTo(disposables)
|
||||
}
|
||||
|
||||
viewModel
|
||||
.identityRecordsObservable
|
||||
|
||||
@@ -29,9 +29,25 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import io.reactivex.rxjava3.subjects.Subject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flatMap
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.rx3.asFlow
|
||||
import org.signal.core.util.concurrent.subscribeWithSubject
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.paging.ProxyPagingController
|
||||
import org.thoughtcrime.securesms.banner.Banner
|
||||
import org.thoughtcrime.securesms.banner.banners.BubbleOptOutBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.GroupsV1MigrationSuggestionsBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.PendingGroupJoinRequestsBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.PendingGroupJoinRequestsBanner.Producer
|
||||
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.contactshare.Contact
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
@@ -42,6 +58,7 @@ import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
|
||||
import org.thoughtcrime.securesms.conversation.v2.items.ChatColorsDrawable
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.MessageTable
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.Mention
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
@@ -155,6 +172,8 @@ class ConversationViewModel(
|
||||
private val refreshReminder: Subject<Unit> = PublishSubject.create()
|
||||
val reminder: Observable<Optional<Reminder>>
|
||||
|
||||
private val groupRecordFlow: Flow<GroupRecord?>
|
||||
|
||||
private val refreshIdentityRecords: Subject<Unit> = PublishSubject.create()
|
||||
private val identityRecordsStore: RxStore<IdentityRecordsState> = RxStore(IdentityRecordsState())
|
||||
val identityRecordsObservable: Observable<IdentityRecordsState> = identityRecordsStore.stateFlowable.toObservable()
|
||||
@@ -276,6 +295,8 @@ class ConversationViewModel(
|
||||
.flatMapMaybe { groupRecord -> repository.getReminder(groupRecord.orNull()) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
groupRecordFlow = recipientRepository.groupRecord.subscribeOn(Schedulers.io()).asFlow().map { it.orNull() }
|
||||
|
||||
Observable.combineLatest(
|
||||
refreshIdentityRecords.startWithItem(Unit).observeOn(Schedulers.io()),
|
||||
recipient,
|
||||
@@ -299,6 +320,36 @@ class ConversationViewModel(
|
||||
})
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun getBannerFlows(context: Context, groupJoinClickListener: () -> Unit, onAddMembers: () -> Unit, onNoThanks: () -> Unit, bubbleClickListener: (Boolean) -> Unit): List<Flow<Banner>> {
|
||||
val pendingGroupJoinFlow = groupRecordFlow.flatMapConcat {
|
||||
flow {
|
||||
if (it == null) {
|
||||
emit(PendingGroupJoinRequestsBanner(false, 0, {}, {}))
|
||||
} else {
|
||||
emitAll(Producer(it.actionableRequestingMembersCount, groupJoinClickListener).flow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val groupV1SuggestionsFlow = groupRecordFlow.map {
|
||||
if (it == null) {
|
||||
GroupsV1MigrationSuggestionsBanner(0, {}, {})
|
||||
} else {
|
||||
GroupsV1MigrationSuggestionsBanner(it.gv1MigrationSuggestions.size, onAddMembers, onNoThanks)
|
||||
}
|
||||
}
|
||||
|
||||
return listOf(
|
||||
OutdatedBuildBanner.createFlow(context, OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
|
||||
UnauthorizedBanner.createFlow(context),
|
||||
ServiceOutageBanner.createFlow(context),
|
||||
pendingGroupJoinFlow,
|
||||
groupV1SuggestionsFlow,
|
||||
BubbleOptOutBanner.createFlow(inBubble = true, bubbleClickListener)
|
||||
)
|
||||
}
|
||||
|
||||
fun onChatBoundsChanged(bounds: Rect) {
|
||||
chatBounds.onNext(bounds)
|
||||
}
|
||||
|
||||
@@ -97,8 +97,14 @@ import org.thoughtcrime.securesms.badges.self.expired.ExpiredOneTimeBadgeBottomS
|
||||
import org.thoughtcrime.securesms.badges.self.expired.MonthlyDonationCanceledBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.banner.Banner;
|
||||
import org.thoughtcrime.securesms.banner.BannerManager;
|
||||
import org.thoughtcrime.securesms.banner.banners.CdsPermanentErrorBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.CdsTemporaryErrorBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.DozeBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.MediaRestoreProgressBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.UsernameOutOfSyncBanner;
|
||||
import org.thoughtcrime.securesms.components.DeleteSyncEducationDialog;
|
||||
import org.thoughtcrime.securesms.components.Material3SearchToolbar;
|
||||
import org.thoughtcrime.securesms.components.RatingManager;
|
||||
@@ -204,6 +210,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlinx.coroutines.flow.Flow;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
@@ -423,7 +430,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
initializeListAdapters();
|
||||
initializeTypingObserver();
|
||||
initializeVoiceNotePlayer();
|
||||
initializeBanners();
|
||||
if (RemoteConfig.newBannerUi()) {
|
||||
initializeBanners();
|
||||
maybeScheduleRefreshProfileJob();
|
||||
}
|
||||
|
||||
RatingManager.showRatingDialogIfNecessary(requireContext());
|
||||
|
||||
@@ -882,11 +892,30 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
}
|
||||
|
||||
private void initializeBanners() {
|
||||
if (RemoteConfig.newBannerUi()) {
|
||||
final List<Flow<? extends Banner>> bannerRepositories = List.of(OutdatedBuildBanner.createFlow(requireContext()),
|
||||
MediaRestoreProgressBanner.createLifecycleAwareFlow(getViewLifecycleOwner()));
|
||||
final BannerManager bannerManager = new BannerManager(bannerRepositories);
|
||||
bannerManager.setContent(bannerView.get());
|
||||
final List<Flow<? extends Banner>> bannerRepositories = List.of(OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
|
||||
UnauthorizedBanner.createFlow(requireContext()),
|
||||
ServiceOutageBanner.createFlow(requireContext()),
|
||||
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.OUTDATED_ONLY),
|
||||
DozeBanner.createFlow(requireContext()),
|
||||
CdsTemporaryErrorBanner.createFlow(getChildFragmentManager()),
|
||||
CdsPermanentErrorBanner.createFlow(getChildFragmentManager()),
|
||||
UsernameOutOfSyncBanner.createFlow(requireContext(), usernameCorruptedToo -> {
|
||||
if (usernameCorruptedToo) {
|
||||
startActivityForResult(AppSettingsActivity.usernameRecovery(requireContext()), UsernameEditFragment.REQUEST_CODE);
|
||||
} else {
|
||||
startActivity(AppSettingsActivity.usernameLinkSettings(requireContext()));
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
}),
|
||||
MediaRestoreProgressBanner.createLifecycleAwareFlow(getViewLifecycleOwner()));
|
||||
final BannerManager bannerManager = new BannerManager(bannerRepositories);
|
||||
bannerManager.setContent(bannerView.get());
|
||||
}
|
||||
|
||||
private void maybeScheduleRefreshProfileJob() {
|
||||
switch (SignalStore.account().getUsernameSyncState()) {
|
||||
case USERNAME_AND_LINK_CORRUPTED, LINK_CORRUPTED -> AppDependencies.getJobManager().add(new RefreshOwnProfileJob());
|
||||
case IN_SYNC -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.thoughtcrime.securesms.registration.data
|
||||
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
|
||||
@@ -44,11 +43,11 @@ object LocalRegistrationMetadataUtil {
|
||||
}.build()
|
||||
}
|
||||
|
||||
fun LocalRegistrationMetadata.getAciIdentityKeyPair() : IdentityKeyPair {
|
||||
fun LocalRegistrationMetadata.getAciIdentityKeyPair(): IdentityKeyPair {
|
||||
return IdentityKeyPair(aciIdentityKeyPair.toByteArray())
|
||||
}
|
||||
|
||||
fun LocalRegistrationMetadata.getPniIdentityKeyPair() : IdentityKeyPair {
|
||||
fun LocalRegistrationMetadata.getPniIdentityKeyPair(): IdentityKeyPair {
|
||||
return IdentityKeyPair(pniIdentityKeyPair.toByteArray())
|
||||
}
|
||||
|
||||
|
||||
@@ -399,7 +399,7 @@ class RegistrationViewModel : ViewModel() {
|
||||
allowedToRequestCode = networkResult.body.allowedToRequestCode,
|
||||
challengesRequested = Challenge.parse(networkResult.body.requestedInformation),
|
||||
verified = networkResult.body.verified,
|
||||
inProgress = false
|
||||
inProgress = false
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -408,7 +408,7 @@ class RegistrationViewModel : ViewModel() {
|
||||
store.update {
|
||||
it.copy(
|
||||
sessionCreationError = error,
|
||||
inProgress = false
|
||||
inProgress = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.core.view.ViewCompat
|
||||
@@ -29,6 +30,9 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.MainActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.banner.BannerManager
|
||||
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
|
||||
import org.thoughtcrime.securesms.components.Material3SearchToolbar
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
@@ -62,6 +66,7 @@ import org.thoughtcrime.securesms.stories.tabs.ConversationListTab
|
||||
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||
import org.thoughtcrime.securesms.util.PlayStoreUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.fragments.requireListener
|
||||
@@ -82,6 +87,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
||||
private lateinit var cameraFab: FloatingActionButton
|
||||
|
||||
private lateinit var reminderView: Stub<ReminderView>
|
||||
private lateinit var bannerView: Stub<ComposeView>
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
@@ -144,6 +150,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
reminderView = ViewUtil.findStubById(view, R.id.reminder)
|
||||
bannerView = ViewUtil.findStubById(view, R.id.banner_stub)
|
||||
updateReminders()
|
||||
}
|
||||
|
||||
@@ -153,12 +160,21 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
||||
}
|
||||
|
||||
private fun updateReminders() {
|
||||
if (ExpiredBuildReminder.isEligible()) {
|
||||
showReminder(ExpiredBuildReminder(context))
|
||||
} else if (UnauthorizedReminder.isEligible(context)) {
|
||||
showReminder(UnauthorizedReminder())
|
||||
if (RemoteConfig.newBannerUi) {
|
||||
val bannerFlows = listOf(
|
||||
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
|
||||
UnauthorizedBanner.createFlow(requireContext())
|
||||
)
|
||||
val bannerManager = BannerManager(bannerFlows)
|
||||
bannerManager.setContent(bannerView.get())
|
||||
} else {
|
||||
hideReminders()
|
||||
if (ExpiredBuildReminder.isEligible()) {
|
||||
showReminder(ExpiredBuildReminder(context))
|
||||
} else if (UnauthorizedReminder.isEligible(context)) {
|
||||
showReminder(UnauthorizedReminder())
|
||||
} else {
|
||||
hideReminders()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,12 @@
|
||||
android:layout="@layout/conversation_activity_reminderview_stub"
|
||||
app:layout_constraintTop_toTopOf="@id/recycler"/>
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/banner_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inflatedId="@+id/banner_compose_view"
|
||||
android:layout="@layout/conversation_list_banner_view"
|
||||
app:layout_constraintTop_toTopOf="@id/recycler"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -108,4 +108,12 @@
|
||||
android:layout="@layout/stories_landing_reminder_view"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/banner_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inflatedId="@+id/banner_compose_view"
|
||||
android:layout="@layout/conversation_list_banner_view"
|
||||
app:layout_constraintTop_toTopOf="@id/recycler"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -180,6 +180,13 @@
|
||||
android:inflatedId="@+id/review_banner"
|
||||
android:layout="@layout/review_banner_view" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/banner_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inflatedId="@+id/banner_compose_view"
|
||||
android:layout="@layout/conversation_list_banner_view" />
|
||||
|
||||
</org.thoughtcrime.securesms.conversation.v2.ConversationBannerView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
Reference in New Issue
Block a user