kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix a bunch UX bugs for donor badges.
rodzic
5047fc54f2
commit
ca24682366
|
@ -44,8 +44,15 @@ class BadgeImageView @JvmOverloads constructor(
|
||||||
fun setBadgeFromRecipient(recipient: Recipient?, glideRequests: GlideRequests) {
|
fun setBadgeFromRecipient(recipient: Recipient?, glideRequests: GlideRequests) {
|
||||||
if (recipient == null || recipient.badges.isEmpty()) {
|
if (recipient == null || recipient.badges.isEmpty()) {
|
||||||
setBadge(null, glideRequests)
|
setBadge(null, glideRequests)
|
||||||
|
} else if (recipient.isSelf) {
|
||||||
|
val badge = recipient.featuredBadge
|
||||||
|
if (badge == null || !badge.visible || badge.isExpired()) {
|
||||||
|
setBadge(null, glideRequests)
|
||||||
|
} else {
|
||||||
|
setBadge(badge, glideRequests)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setBadge(recipient.badges[0], glideRequests)
|
setBadge(recipient.featuredBadge, glideRequests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,10 +68,11 @@ class BadgesOverviewFragment : DSLSettingsFragment(
|
||||||
fadedBadgeId = state.fadedBadgeId
|
fadedBadgeId = state.fadedBadgeId
|
||||||
)
|
)
|
||||||
|
|
||||||
switchPref(
|
asyncSwitchPref(
|
||||||
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile),
|
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile),
|
||||||
isChecked = state.displayBadgesOnProfile,
|
isChecked = state.displayBadgesOnProfile,
|
||||||
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges,
|
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges,
|
||||||
|
isProcessing = state.stage == BadgesOverviewState.Stage.UPDATING_BADGE_DISPLAY_STATE,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setDisplayBadgesOnProfile(!state.displayBadgesOnProfile)
|
viewModel.setDisplayBadgesOnProfile(!state.displayBadgesOnProfile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@ data class BadgesOverviewState(
|
||||||
enum class Stage {
|
enum class Stage {
|
||||||
INIT,
|
INIT,
|
||||||
READY,
|
READY,
|
||||||
UPDATING
|
UPDATING_BADGE_DISPLAY_STATE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ class BadgesOverviewViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDisplayBadgesOnProfile(displayBadgesOnProfile: Boolean) {
|
fun setDisplayBadgesOnProfile(displayBadgesOnProfile: Boolean) {
|
||||||
|
store.update { it.copy(stage = BadgesOverviewState.Stage.UPDATING_BADGE_DISPLAY_STATE) }
|
||||||
disposables += badgeRepository.setVisibilityForAllBadges(displayBadgesOnProfile)
|
disposables += badgeRepository.setVisibilityForAllBadges(displayBadgesOnProfile)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.core.content.ContextCompat;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
@ -26,6 +27,9 @@ public class ConversationTypingView extends ConstraintLayout {
|
||||||
private AvatarImageView avatar1;
|
private AvatarImageView avatar1;
|
||||||
private AvatarImageView avatar2;
|
private AvatarImageView avatar2;
|
||||||
private AvatarImageView avatar3;
|
private AvatarImageView avatar3;
|
||||||
|
private BadgeImageView badge1;
|
||||||
|
private BadgeImageView badge2;
|
||||||
|
private BadgeImageView badge3;
|
||||||
private View bubble;
|
private View bubble;
|
||||||
private TypingIndicatorView indicator;
|
private TypingIndicatorView indicator;
|
||||||
private TextView typistCount;
|
private TextView typistCount;
|
||||||
|
@ -41,6 +45,9 @@ public class ConversationTypingView extends ConstraintLayout {
|
||||||
avatar1 = findViewById(R.id.typing_avatar_1);
|
avatar1 = findViewById(R.id.typing_avatar_1);
|
||||||
avatar2 = findViewById(R.id.typing_avatar_2);
|
avatar2 = findViewById(R.id.typing_avatar_2);
|
||||||
avatar3 = findViewById(R.id.typing_avatar_3);
|
avatar3 = findViewById(R.id.typing_avatar_3);
|
||||||
|
badge1 = findViewById(R.id.typing_badge_1);
|
||||||
|
badge2 = findViewById(R.id.typing_badge_2);
|
||||||
|
badge3 = findViewById(R.id.typing_badge_3);
|
||||||
typistCount = findViewById(R.id.typing_count);
|
typistCount = findViewById(R.id.typing_count);
|
||||||
bubble = findViewById(R.id.typing_bubble);
|
bubble = findViewById(R.id.typing_bubble);
|
||||||
indicator = findViewById(R.id.typing_indicator);
|
indicator = findViewById(R.id.typing_indicator);
|
||||||
|
@ -55,6 +62,9 @@ public class ConversationTypingView extends ConstraintLayout {
|
||||||
avatar1.setVisibility(GONE);
|
avatar1.setVisibility(GONE);
|
||||||
avatar2.setVisibility(GONE);
|
avatar2.setVisibility(GONE);
|
||||||
avatar3.setVisibility(GONE);
|
avatar3.setVisibility(GONE);
|
||||||
|
badge1.setVisibility(GONE);
|
||||||
|
badge2.setVisibility(GONE);
|
||||||
|
badge3.setVisibility(GONE);
|
||||||
typistCount.setVisibility(GONE);
|
typistCount.setVisibility(GONE);
|
||||||
|
|
||||||
if (isGroupThread) {
|
if (isGroupThread) {
|
||||||
|
@ -75,15 +85,21 @@ public class ConversationTypingView extends ConstraintLayout {
|
||||||
private void presentGroupThreadAvatars(@NonNull GlideRequests glideRequests, @NonNull List<Recipient> typists) {
|
private void presentGroupThreadAvatars(@NonNull GlideRequests glideRequests, @NonNull List<Recipient> typists) {
|
||||||
avatar1.setAvatar(glideRequests, typists.get(0), typists.size() == 1);
|
avatar1.setAvatar(glideRequests, typists.get(0), typists.size() == 1);
|
||||||
avatar1.setVisibility(VISIBLE);
|
avatar1.setVisibility(VISIBLE);
|
||||||
|
badge1.setBadgeFromRecipient(typists.get(0), glideRequests);
|
||||||
|
badge1.setVisibility(VISIBLE);
|
||||||
|
|
||||||
if (typists.size() > 1) {
|
if (typists.size() > 1) {
|
||||||
avatar2.setAvatar(glideRequests, typists.get(1), false);
|
avatar2.setAvatar(glideRequests, typists.get(1), false);
|
||||||
avatar2.setVisibility(VISIBLE);
|
avatar2.setVisibility(VISIBLE);
|
||||||
|
badge2.setBadgeFromRecipient(typists.get(1), glideRequests);
|
||||||
|
badge2.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typists.size() == 3) {
|
if (typists.size() == 3) {
|
||||||
avatar3.setAvatar(glideRequests, typists.get(2), false);
|
avatar3.setAvatar(glideRequests, typists.get(2), false);
|
||||||
avatar3.setVisibility(VISIBLE);
|
avatar3.setVisibility(VISIBLE);
|
||||||
|
badge3.setBadgeFromRecipient(typists.get(2), glideRequests);
|
||||||
|
badge3.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typists.size() > 3) {
|
if (typists.size() > 3) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.settings.models.AsyncSwitch
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Button
|
import org.thoughtcrime.securesms.components.settings.models.Button
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Space
|
import org.thoughtcrime.securesms.components.settings.models.Space
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Text
|
import org.thoughtcrime.securesms.components.settings.models.Text
|
||||||
|
@ -37,6 +38,7 @@ class DSLSettingsAdapter : MappingAdapter() {
|
||||||
Text.register(this)
|
Text.register(this)
|
||||||
Space.register(this)
|
Space.register(this)
|
||||||
Button.register(this)
|
Button.register(this)
|
||||||
|
AsyncSwitch.register(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -415,6 +415,6 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enqueueSubscriptionRedemption() {
|
private fun enqueueSubscriptionRedemption() {
|
||||||
SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
|
SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,11 +132,10 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
return Completable.create {
|
return Completable.create {
|
||||||
stripeApi.confirmPaymentIntent(GooglePayPaymentSource(paymentData), paymentIntent).blockingSubscribe()
|
stripeApi.confirmPaymentIntent(GooglePayPaymentSource(paymentData), paymentIntent).blockingSubscribe()
|
||||||
|
|
||||||
val jobId = BoostReceiptRequestResponseJob.enqueueChain(paymentIntent)
|
|
||||||
val countDownLatch = CountDownLatch(1)
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
|
||||||
var finalJobState: JobTracker.JobState? = null
|
var finalJobState: JobTracker.JobState? = null
|
||||||
ApplicationDependencies.getJobManager().addListener(jobId) { _, jobState ->
|
|
||||||
|
BoostReceiptRequestResponseJob.createJobChain(paymentIntent).enqueue { _, jobState ->
|
||||||
if (jobState.isComplete) {
|
if (jobState.isComplete) {
|
||||||
finalJobState = jobState
|
finalJobState = jobState
|
||||||
countDownLatch.countDown()
|
countDownLatch.countDown()
|
||||||
|
@ -200,11 +199,10 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
||||||
}
|
}
|
||||||
}.andThen {
|
}.andThen {
|
||||||
Log.d(TAG, "Enqueuing request response job chain.", true)
|
Log.d(TAG, "Enqueuing request response job chain.", true)
|
||||||
val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
|
|
||||||
val countDownLatch = CountDownLatch(1)
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
|
||||||
var finalJobState: JobTracker.JobState? = null
|
var finalJobState: JobTracker.JobState? = null
|
||||||
ApplicationDependencies.getJobManager().addListener(jobId) { _, jobState ->
|
|
||||||
|
SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue { _, jobState ->
|
||||||
if (jobState.isComplete) {
|
if (jobState.isComplete) {
|
||||||
finalJobState = jobState
|
finalJobState = jobState
|
||||||
countDownLatch.countDown()
|
countDownLatch.countDown()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.view.View
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
|
import androidx.core.text.isDigitsOnly
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
@ -25,6 +26,7 @@ import org.thoughtcrime.securesms.util.MappingAdapter
|
||||||
import org.thoughtcrime.securesms.util.MappingViewHolder
|
import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
import java.lang.Integer.min
|
import java.lang.Integer.min
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
@ -137,7 +139,10 @@ data class Boost(
|
||||||
button.text = FiatMoneyUtil.format(
|
button.text = FiatMoneyUtil.format(
|
||||||
context.resources,
|
context.resources,
|
||||||
boost.price,
|
boost.price,
|
||||||
FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()
|
FiatMoneyUtil
|
||||||
|
.formatOptions()
|
||||||
|
.numberOnly()
|
||||||
|
.trimZerosAfterDecimal()
|
||||||
)
|
)
|
||||||
button.setOnClickListener {
|
button.setOnClickListener {
|
||||||
model.onBoostClick(it, boost)
|
model.onBoostClick(it, boost)
|
||||||
|
@ -181,11 +186,12 @@ data class Boost(
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
class MoneyFilter(val currency: Currency, private val onCustomAmountChanged: (String) -> Unit = {}) : DigitsKeyListener(), TextWatcher {
|
class MoneyFilter(val currency: Currency, private val onCustomAmountChanged: (String) -> Unit = {}) : DigitsKeyListener(false, true), TextWatcher {
|
||||||
|
|
||||||
|
val separator = DecimalFormatSymbols.getInstance().decimalSeparator
|
||||||
val separatorCount = min(1, currency.defaultFractionDigits)
|
val separatorCount = min(1, currency.defaultFractionDigits)
|
||||||
val prefix: String = currency.getSymbol(Locale.getDefault())
|
val prefix: String = currency.getSymbol(Locale.getDefault())
|
||||||
val pattern: Pattern = "[0-9]*([.,]){0,$separatorCount}[0-9]{0,${currency.defaultFractionDigits}}".toPattern()
|
val pattern: Pattern = "[0-9]*($separator){0,$separatorCount}[0-9]{0,${currency.defaultFractionDigits}}".toPattern()
|
||||||
|
|
||||||
override fun filter(
|
override fun filter(
|
||||||
source: CharSequence,
|
source: CharSequence,
|
||||||
|
@ -198,6 +204,11 @@ data class Boost(
|
||||||
|
|
||||||
val result = dest.subSequence(0, dstart).toString() + source.toString() + dest.subSequence(dend, dest.length)
|
val result = dest.subSequence(0, dstart).toString() + source.toString() + dest.subSequence(dend, dest.length)
|
||||||
val resultWithoutCurrencyPrefix = result.removePrefix(prefix)
|
val resultWithoutCurrencyPrefix = result.removePrefix(prefix)
|
||||||
|
|
||||||
|
if (result.length == 1 && !result.isDigitsOnly() && result != separator.toString()) {
|
||||||
|
return dest.subSequence(dstart, dend)
|
||||||
|
}
|
||||||
|
|
||||||
val matcher = pattern.matcher(resultWithoutCurrencyPrefix)
|
val matcher = pattern.matcher(resultWithoutCurrencyPrefix)
|
||||||
|
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
|
|
|
@ -132,6 +132,11 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
processingDonationPaymentDialog.hide()
|
||||||
|
}
|
||||||
|
|
||||||
private fun getConfiguration(state: BoostState): DSLConfiguration {
|
private fun getConfiguration(state: BoostState): DSLConfiguration {
|
||||||
if (state.stage == BoostState.Stage.PAYMENT_PIPELINE) {
|
if (state.stage == BoostState.Stage.PAYMENT_PIPELINE) {
|
||||||
processingDonationPaymentDialog.show()
|
processingDonationPaymentDialog.show()
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
import org.thoughtcrime.securesms.util.livedata.Store
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
|
|
||||||
class BoostViewModel(
|
class BoostViewModel(
|
||||||
|
@ -190,7 +191,7 @@ class BoostViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCustomAmount(amount: String) {
|
fun setCustomAmount(amount: String) {
|
||||||
val bigDecimalAmount = if (amount.isEmpty()) {
|
val bigDecimalAmount = if (amount.isEmpty() || amount == DecimalFormatSymbols.getInstance().decimalSeparator.toString()) {
|
||||||
BigDecimal.ZERO
|
BigDecimal.ZERO
|
||||||
} else {
|
} else {
|
||||||
BigDecimal(amount)
|
BigDecimal(amount)
|
||||||
|
|
|
@ -109,6 +109,11 @@ class SubscribeFragment : DSLSettingsFragment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
processingDonationPaymentDialog.hide()
|
||||||
|
}
|
||||||
|
|
||||||
private fun getConfiguration(state: SubscribeState): DSLConfiguration {
|
private fun getConfiguration(state: SubscribeState): DSLConfiguration {
|
||||||
if (state.hasInProgressSubscriptionTransaction || state.stage == SubscribeState.Stage.PAYMENT_PIPELINE) {
|
if (state.hasInProgressSubscriptionTransaction || state.stage == SubscribeState.Stage.PAYMENT_PIPELINE) {
|
||||||
processingDonationPaymentDialog.show()
|
processingDonationPaymentDialog.show()
|
||||||
|
|
|
@ -108,6 +108,7 @@ class ThanksForYourSupportBottomSheetDialogFragment : FixedRoundedCornerBottomSh
|
||||||
|
|
||||||
if (args.isBoost) {
|
if (args.isBoost) {
|
||||||
presentBoostCopy()
|
presentBoostCopy()
|
||||||
|
badgeView.visibility = View.INVISIBLE
|
||||||
lottie.visible = true
|
lottie.visible = true
|
||||||
lottie.playAnimation()
|
lottie.playAnimation()
|
||||||
lottie.addAnimatorListener(object : AnimationCompleteListener() {
|
lottie.addAnimatorListener(object : AnimationCompleteListener() {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import org.thoughtcrime.securesms.components.settings.models.AsyncSwitch
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Button
|
import org.thoughtcrime.securesms.components.settings.models.Button
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Space
|
import org.thoughtcrime.securesms.components.settings.models.Space
|
||||||
import org.thoughtcrime.securesms.components.settings.models.Text
|
import org.thoughtcrime.securesms.components.settings.models.Text
|
||||||
|
@ -56,6 +57,17 @@ class DSLConfiguration {
|
||||||
children.add(preference)
|
children.add(preference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun asyncSwitchPref(
|
||||||
|
title: DSLSettingsText,
|
||||||
|
isEnabled: Boolean = true,
|
||||||
|
isChecked: Boolean,
|
||||||
|
isProcessing: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val preference = AsyncSwitch.Model(title, isEnabled, isChecked, isProcessing, onClick)
|
||||||
|
children.add(preference)
|
||||||
|
}
|
||||||
|
|
||||||
fun switchPref(
|
fun switchPref(
|
||||||
title: DSLSettingsText,
|
title: DSLSettingsText,
|
||||||
summary: DSLSettingsText? = null,
|
summary: DSLSettingsText? = null,
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package org.thoughtcrime.securesms.components.settings.models
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ViewSwitcher
|
||||||
|
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||||
|
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||||
|
import org.thoughtcrime.securesms.components.settings.PreferenceViewHolder
|
||||||
|
import org.thoughtcrime.securesms.util.MappingAdapter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch that will perform a long-running async operation (normally network) that requires a
|
||||||
|
* progress spinner to replace the switch after a press.
|
||||||
|
*/
|
||||||
|
object AsyncSwitch {
|
||||||
|
|
||||||
|
fun register(adapter: MappingAdapter) {
|
||||||
|
adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory(AsyncSwitch::ViewHolder, R.layout.dsl_async_switch_preference_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Model(
|
||||||
|
override val title: DSLSettingsText,
|
||||||
|
override val isEnabled: Boolean,
|
||||||
|
val isChecked: Boolean,
|
||||||
|
val isProcessing: Boolean,
|
||||||
|
val onClick: () -> Unit
|
||||||
|
) : PreferenceModel<Model>() {
|
||||||
|
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||||
|
return super.areContentsTheSame(newItem) && isChecked == newItem.isChecked && isProcessing == newItem.isProcessing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(itemView: View) : PreferenceViewHolder<Model>(itemView) {
|
||||||
|
private val switchWidget: SwitchMaterial = itemView.findViewById(R.id.switch_widget)
|
||||||
|
private val switcher: ViewSwitcher = itemView.findViewById(R.id.switcher)
|
||||||
|
|
||||||
|
override fun bind(model: Model) {
|
||||||
|
super.bind(model)
|
||||||
|
switchWidget.isEnabled = model.isEnabled
|
||||||
|
switchWidget.isChecked = model.isChecked
|
||||||
|
itemView.isEnabled = !model.isProcessing
|
||||||
|
switcher.displayedChild = if (model.isProcessing) 1 else 0
|
||||||
|
|
||||||
|
itemView.setOnClickListener {
|
||||||
|
if (!model.isProcessing) {
|
||||||
|
itemView.isEnabled = false
|
||||||
|
switcher.displayedChild = 1
|
||||||
|
model.onClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
import org.signal.core.util.ThreadUtil;
|
import org.signal.core.util.ThreadUtil;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||||
|
@ -55,9 +56,11 @@ public class CallParticipantView extends ConstraintLayout {
|
||||||
|
|
||||||
private AppCompatImageView backgroundAvatar;
|
private AppCompatImageView backgroundAvatar;
|
||||||
private AvatarImageView avatar;
|
private AvatarImageView avatar;
|
||||||
|
private BadgeImageView badge;
|
||||||
private View rendererFrame;
|
private View rendererFrame;
|
||||||
private TextureViewRenderer renderer;
|
private TextureViewRenderer renderer;
|
||||||
private ImageView pipAvatar;
|
private ImageView pipAvatar;
|
||||||
|
private BadgeImageView pipBadge;
|
||||||
private ContactPhoto contactPhoto;
|
private ContactPhoto contactPhoto;
|
||||||
private View audioMuted;
|
private View audioMuted;
|
||||||
private View infoOverlay;
|
private View infoOverlay;
|
||||||
|
@ -92,6 +95,8 @@ public class CallParticipantView extends ConstraintLayout {
|
||||||
infoIcon = findViewById(R.id.call_participant_info_icon);
|
infoIcon = findViewById(R.id.call_participant_info_icon);
|
||||||
infoMessage = findViewById(R.id.call_participant_info_message);
|
infoMessage = findViewById(R.id.call_participant_info_message);
|
||||||
infoMoreInfo = findViewById(R.id.call_participant_info_more_info);
|
infoMoreInfo = findViewById(R.id.call_participant_info_more_info);
|
||||||
|
badge = findViewById(R.id.call_participant_item_badge);
|
||||||
|
pipBadge = findViewById(R.id.call_participant_item_pip_badge);
|
||||||
|
|
||||||
avatar.setFallbackPhotoProvider(FALLBACK_PHOTO_PROVIDER);
|
avatar.setFallbackPhotoProvider(FALLBACK_PHOTO_PROVIDER);
|
||||||
useLargeAvatar();
|
useLargeAvatar();
|
||||||
|
@ -120,7 +125,9 @@ public class CallParticipantView extends ConstraintLayout {
|
||||||
renderer.attachBroadcastVideoSink(null);
|
renderer.attachBroadcastVideoSink(null);
|
||||||
audioMuted.setVisibility(View.GONE);
|
audioMuted.setVisibility(View.GONE);
|
||||||
avatar.setVisibility(View.GONE);
|
avatar.setVisibility(View.GONE);
|
||||||
|
badge.setVisibility(View.GONE);
|
||||||
pipAvatar.setVisibility(View.GONE);
|
pipAvatar.setVisibility(View.GONE);
|
||||||
|
pipBadge.setVisibility(View.GONE);
|
||||||
|
|
||||||
infoOverlay.setVisibility(View.VISIBLE);
|
infoOverlay.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
@ -157,8 +164,10 @@ public class CallParticipantView extends ConstraintLayout {
|
||||||
|
|
||||||
if (participantChanged || !Objects.equals(contactPhoto, participant.getRecipient().getContactPhoto())) {
|
if (participantChanged || !Objects.equals(contactPhoto, participant.getRecipient().getContactPhoto())) {
|
||||||
avatar.setAvatarUsingProfile(participant.getRecipient());
|
avatar.setAvatarUsingProfile(participant.getRecipient());
|
||||||
|
badge.setBadgeFromRecipient(participant.getRecipient());
|
||||||
AvatarUtil.loadBlurredIconIntoImageView(participant.getRecipient(), backgroundAvatar);
|
AvatarUtil.loadBlurredIconIntoImageView(participant.getRecipient(), backgroundAvatar);
|
||||||
setPipAvatar(participant.getRecipient());
|
setPipAvatar(participant.getRecipient());
|
||||||
|
pipBadge.setBadgeFromRecipient(participant.getRecipient());
|
||||||
contactPhoto = participant.getRecipient().getContactPhoto();
|
contactPhoto = participant.getRecipient().getContactPhoto();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,15 +202,19 @@ public class CallParticipantView extends ConstraintLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
avatar.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE);
|
avatar.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE);
|
||||||
|
badge.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE);
|
||||||
pipAvatar.setVisibility(shouldRenderInPip ? View.VISIBLE : View.GONE);
|
pipAvatar.setVisibility(shouldRenderInPip ? View.VISIBLE : View.GONE);
|
||||||
|
pipBadge.setVisibility(shouldRenderInPip ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hideAvatar() {
|
void hideAvatar() {
|
||||||
avatar.setAlpha(0f);
|
avatar.setAlpha(0f);
|
||||||
|
badge.setAlpha(0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showAvatar() {
|
void showAvatar() {
|
||||||
avatar.setAlpha(1f);
|
avatar.setAlpha(1f);
|
||||||
|
badge.setAlpha(1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void useLargeAvatar() {
|
void useLargeAvatar() {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
|
@ -29,6 +30,7 @@ public class CallParticipantsListUpdatePopupWindow extends PopupWindow {
|
||||||
|
|
||||||
private final ViewGroup parent;
|
private final ViewGroup parent;
|
||||||
private final AvatarImageView avatarImageView;
|
private final AvatarImageView avatarImageView;
|
||||||
|
private final BadgeImageView badgeImageView;
|
||||||
private final TextView descriptionTextView;
|
private final TextView descriptionTextView;
|
||||||
|
|
||||||
private final Set<CallParticipantListUpdate.Wrapper> pendingAdditions = new HashSet<>();
|
private final Set<CallParticipantListUpdate.Wrapper> pendingAdditions = new HashSet<>();
|
||||||
|
@ -43,6 +45,7 @@ public class CallParticipantsListUpdatePopupWindow extends PopupWindow {
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.avatarImageView = getContentView().findViewById(R.id.avatar);
|
this.avatarImageView = getContentView().findViewById(R.id.avatar);
|
||||||
|
this.badgeImageView = getContentView().findViewById(R.id.badge);
|
||||||
this.descriptionTextView = getContentView().findViewById(R.id.description);
|
this.descriptionTextView = getContentView().findViewById(R.id.description);
|
||||||
|
|
||||||
setOnDismissListener(this::showPending);
|
setOnDismissListener(this::showPending);
|
||||||
|
@ -109,6 +112,7 @@ public class CallParticipantsListUpdatePopupWindow extends PopupWindow {
|
||||||
|
|
||||||
private void setAvatar(@Nullable Recipient recipient) {
|
private void setAvatar(@Nullable Recipient recipient) {
|
||||||
avatarImageView.setAvatarUsingProfile(recipient);
|
avatarImageView.setAvatarUsingProfile(recipient);
|
||||||
|
badgeImageView.setBadgeFromRecipient(recipient);
|
||||||
avatarImageView.setVisibility(recipient == null ? View.GONE : View.VISIBLE);
|
avatarImageView.setVisibility(recipient == null ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -322,6 +322,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
|
|
||||||
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), Recipient::self, this::initializeProfileIcon);
|
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), Recipient::self, this::initializeProfileIcon);
|
||||||
|
|
||||||
|
initializeSettingsTouchTarget();
|
||||||
|
|
||||||
if ((!searchToolbar.resolved() || !searchToolbar.get().isVisible()) && list.getAdapter() != defaultAdapter) {
|
if ((!searchToolbar.resolved() || !searchToolbar.get().isVisible()) && list.getAdapter() != defaultAdapter) {
|
||||||
list.removeItemDecoration(searchAdapterDecoration);
|
list.removeItemDecoration(searchAdapterDecoration);
|
||||||
setAdapter(defaultAdapter);
|
setAdapter(defaultAdapter);
|
||||||
|
@ -527,7 +529,11 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
imageView.setBadgeFromRecipient(recipient);
|
imageView.setBadgeFromRecipient(recipient);
|
||||||
|
|
||||||
AvatarUtil.loadIconIntoImageView(recipient, icon, getResources().getDimensionPixelSize(R.dimen.toolbar_avatar_size));
|
AvatarUtil.loadIconIntoImageView(recipient, icon, getResources().getDimensionPixelSize(R.dimen.toolbar_avatar_size));
|
||||||
icon.setOnClickListener(v -> getNavigator().goToAppSettings());
|
}
|
||||||
|
|
||||||
|
private void initializeSettingsTouchTarget() {
|
||||||
|
View touchArea = requireView().findViewById(R.id.toolbar_settings_touch_area);
|
||||||
|
touchArea.setOnClickListener(v -> getNavigator().goToAppSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeSearchListener() {
|
private void initializeSearchListener() {
|
||||||
|
|
|
@ -226,6 +226,8 @@ public final class ConversationListItem extends ConstraintLayout
|
||||||
private void setBadgeFromRecipient(Recipient recipient) {
|
private void setBadgeFromRecipient(Recipient recipient) {
|
||||||
if (!recipient.isSelf()) {
|
if (!recipient.isSelf()) {
|
||||||
badge.setBadgeFromRecipient(recipient);
|
badge.setBadgeFromRecipient(recipient);
|
||||||
|
} else {
|
||||||
|
badge.setBadge(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,14 @@ public class JobManager implements ConstraintObserver.Notifier {
|
||||||
jobManager.enqueueChain(this);
|
jobManager.enqueueChain(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enqueue(@NonNull JobTracker.JobListener listener) {
|
||||||
|
List<Job> lastChain = jobs.get(jobs.size() - 1);
|
||||||
|
Job lastJobInLastChain = lastChain.get(lastChain.size() - 1);
|
||||||
|
|
||||||
|
jobManager.addListener(lastJobInLastChain.getId(), listener);
|
||||||
|
enqueue();
|
||||||
|
}
|
||||||
|
|
||||||
private List<List<Job>> getJobListChain() {
|
private List<List<Job>> getJobListChain() {
|
||||||
return jobs;
|
return jobs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.signal.zkgroup.receipts.ReceiptSerial;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.subscription.SubscriptionNotification;
|
import org.thoughtcrime.securesms.subscription.SubscriptionNotification;
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||||
|
@ -56,18 +57,15 @@ public class BoostReceiptRequestResponseJob extends BaseJob {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String enqueueChain(StripeApi.PaymentIntent paymentIntent) {
|
public static JobManager.Chain createJobChain(StripeApi.PaymentIntent paymentIntent) {
|
||||||
BoostReceiptRequestResponseJob requestReceiptJob = createJob(paymentIntent);
|
BoostReceiptRequestResponseJob requestReceiptJob = createJob(paymentIntent);
|
||||||
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForBoost();
|
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForBoost();
|
||||||
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
||||||
|
|
||||||
ApplicationDependencies.getJobManager()
|
return ApplicationDependencies.getJobManager()
|
||||||
.startChain(requestReceiptJob)
|
.startChain(requestReceiptJob)
|
||||||
.then(redeemReceiptJob)
|
.then(redeemReceiptJob)
|
||||||
.then(refreshOwnProfileJob)
|
.then(refreshOwnProfileJob);
|
||||||
.enqueue();
|
|
||||||
|
|
||||||
return refreshOwnProfileJob.getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BoostReceiptRequestResponseJob(@NonNull Parameters parameters,
|
private BoostReceiptRequestResponseJob(@NonNull Parameters parameters,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
|
import org.thoughtcrime.securesms.subscription.SubscriptionNotification;
|
||||||
import org.whispersystems.signalservice.internal.EmptyResponse;
|
import org.whispersystems.signalservice.internal.EmptyResponse;
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ public class DonationReceiptRedemptionJob extends BaseJob {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure() {
|
public void onFailure() {
|
||||||
|
SubscriptionNotification.RedemptionFailed.INSTANCE.show(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,8 +72,8 @@ public class DonationReceiptRedemptionJob extends BaseJob {
|
||||||
Data inputData = getInputData();
|
Data inputData = getInputData();
|
||||||
|
|
||||||
if (inputData == null) {
|
if (inputData == null) {
|
||||||
Log.w(TAG, "No input data. Failing.", null, true);
|
Log.w(TAG, "No input data. Exiting.", null, true);
|
||||||
throw new IllegalStateException("Expected a presentation object in input data.");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] presentationBytes = inputData.getStringAsBlob(INPUT_RECEIPT_CREDENTIAL_PRESENTATION);
|
byte[] presentationBytes = inputData.getStringAsBlob(INPUT_RECEIPT_CREDENTIAL_PRESENTATION);
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.subscription.Subscriber;
|
import org.thoughtcrime.securesms.subscription.Subscriber;
|
||||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription;
|
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription;
|
||||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId;
|
|
||||||
import org.whispersystems.signalservice.internal.EmptyResponse;
|
import org.whispersystems.signalservice.internal.EmptyResponse;
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ public class SubscriptionKeepAliveJob extends BaseJob {
|
||||||
|
|
||||||
if (activeSubscription.getActiveSubscription().getEndOfCurrentPeriod() > SignalStore.donationsValues().getLastEndOfPeriod()) {
|
if (activeSubscription.getActiveSubscription().getEndOfCurrentPeriod() > SignalStore.donationsValues().getLastEndOfPeriod()) {
|
||||||
Log.i(TAG, "Last end of period change. Requesting receipt refresh.");
|
Log.i(TAG, "Last end of period change. Requesting receipt refresh.");
|
||||||
SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation();
|
SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.signal.zkgroup.receipts.ReceiptSerial;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.subscription.Subscriber;
|
import org.thoughtcrime.securesms.subscription.Subscriber;
|
||||||
|
@ -62,19 +63,16 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String enqueueSubscriptionContinuation() {
|
public static JobManager.Chain createSubscriptionContinuationJobChain() {
|
||||||
Subscriber subscriber = SignalStore.donationsValues().requireSubscriber();
|
Subscriber subscriber = SignalStore.donationsValues().requireSubscriber();
|
||||||
SubscriptionReceiptRequestResponseJob requestReceiptJob = createJob(subscriber.getSubscriberId());
|
SubscriptionReceiptRequestResponseJob requestReceiptJob = createJob(subscriber.getSubscriberId());
|
||||||
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForSubscription();
|
DonationReceiptRedemptionJob redeemReceiptJob = DonationReceiptRedemptionJob.createJobForSubscription();
|
||||||
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
RefreshOwnProfileJob refreshOwnProfileJob = new RefreshOwnProfileJob();
|
||||||
|
|
||||||
ApplicationDependencies.getJobManager()
|
return ApplicationDependencies.getJobManager()
|
||||||
.startChain(requestReceiptJob)
|
.startChain(requestReceiptJob)
|
||||||
.then(redeemReceiptJob)
|
.then(redeemReceiptJob)
|
||||||
.then(refreshOwnProfileJob)
|
.then(refreshOwnProfileJob);
|
||||||
.enqueue();
|
|
||||||
|
|
||||||
return refreshOwnProfileJob.getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubscriptionReceiptRequestResponseJob(@NonNull Parameters parameters,
|
private SubscriptionReceiptRequestResponseJob(@NonNull Parameters parameters,
|
||||||
|
|
|
@ -226,7 +226,11 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presentBadge(@NonNull Optional<Badge> badge) {
|
private void presentBadge(@NonNull Optional<Badge> badge) {
|
||||||
badgeView.setBadge(badge.orNull());
|
if (badge.isPresent() && badge.get().getVisible() && !badge.get().isExpired()) {
|
||||||
|
badgeView.setBadge(badge.orNull());
|
||||||
|
} else {
|
||||||
|
badgeView.setBadge(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void presentEvent(@NonNull ManageProfileViewModel.Event event) {
|
private void presentEvent(@NonNull ManageProfileViewModel.Event event) {
|
||||||
|
|
|
@ -38,5 +38,31 @@ sealed class SubscriptionNotification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object RedemptionFailed : SubscriptionNotification() {
|
||||||
|
override fun show(context: Context) {
|
||||||
|
val notification = NotificationCompat.Builder(context, NotificationChannels.FAILURES)
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setContentTitle(context.getString(R.string.Subscription__redemption_failed))
|
||||||
|
.setContentText(context.getString(R.string.Subscription__please_contact_support_for_more_information))
|
||||||
|
.addAction(
|
||||||
|
NotificationCompat.Action.Builder(
|
||||||
|
null,
|
||||||
|
context.getString(R.string.Subscription__contact_support),
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
AppSettingsActivity.help(context, HelpFragment.DONATION_INDEX),
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_ONE_SHOT else 0
|
||||||
|
)
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
NotificationManagerCompat
|
||||||
|
.from(context)
|
||||||
|
.notify(NotificationIds.SUBSCRIPTION_VERIFY_FAILED, notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract fun show(context: Context)
|
abstract fun show(context: Context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.MappingAdapter;
|
import org.thoughtcrime.securesms.util.MappingAdapter;
|
||||||
|
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.util.MappingViewHolder;
|
||||||
public class RecipientViewHolder<T extends RecipientMappingModel<T>> extends MappingViewHolder<T> {
|
public class RecipientViewHolder<T extends RecipientMappingModel<T>> extends MappingViewHolder<T> {
|
||||||
|
|
||||||
protected final @Nullable AvatarImageView avatar;
|
protected final @Nullable AvatarImageView avatar;
|
||||||
|
protected final @Nullable BadgeImageView badge;
|
||||||
protected final @Nullable TextView name;
|
protected final @Nullable TextView name;
|
||||||
protected final @Nullable EventListener<T> eventListener;
|
protected final @Nullable EventListener<T> eventListener;
|
||||||
private final boolean quickContactEnabled;
|
private final boolean quickContactEnabled;
|
||||||
|
@ -30,6 +32,7 @@ public class RecipientViewHolder<T extends RecipientMappingModel<T>> extends Map
|
||||||
this.quickContactEnabled = quickContactEnabled;
|
this.quickContactEnabled = quickContactEnabled;
|
||||||
|
|
||||||
avatar = findViewById(R.id.recipient_view_avatar);
|
avatar = findViewById(R.id.recipient_view_avatar);
|
||||||
|
badge = findViewById(R.id.recipient_view_badge);
|
||||||
name = findViewById(R.id.recipient_view_name);
|
name = findViewById(R.id.recipient_view_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +42,10 @@ public class RecipientViewHolder<T extends RecipientMappingModel<T>> extends Map
|
||||||
avatar.setRecipient(model.getRecipient(), quickContactEnabled);
|
avatar.setRecipient(model.getRecipient(), quickContactEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (badge != null) {
|
||||||
|
badge.setBadgeFromRecipient(model.getRecipient());
|
||||||
|
}
|
||||||
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
name.setText(model.getName(context));
|
name.setText(model.getName(context));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,21 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@tools:sample/avatars" />
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/call_participant_badge_offseter"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/call_participant_item_avatar"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/call_participant_item_avatar" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/call_participant_item_badge"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
app:badge_size="large"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/call_participant_badge_offseter"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/call_participant_badge_offseter" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/call_participant_item_pip_avatar"
|
android:id="@+id/call_participant_item_pip_avatar"
|
||||||
android:layout_width="200dp"
|
android:layout_width="200dp"
|
||||||
|
@ -39,6 +54,15 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@tools:sample/avatars" />
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/call_participant_item_pip_badge"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:badge_size="large"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/call_participant_item_pip_avatar"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/call_participant_item_pip_avatar" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/call_participant_renderer_frame"
|
android:id="@+id/call_participant_renderer_frame"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -26,6 +26,18 @@
|
||||||
tools:src="@drawable/ic_profile_80"
|
tools:src="@drawable/ic_profile_80"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/badge"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="22dp"
|
||||||
|
android:contentDescription="@string/ImageView__badge"
|
||||||
|
app:badge_size="medium"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/description"
|
android:id="@+id/description"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -16,13 +16,25 @@
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
app:fallbackImageSize="small"
|
app:fallbackImageSize="small"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/recipient_view_badge"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="23dp"
|
||||||
|
app:badge_size="small"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/recipient_view_avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/recipient_view_avatar" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/recipient_view_name"
|
android:id="@+id/recipient_view_name"
|
||||||
style="@style/TextAppearance.Signal.Body1"
|
style="@style/TextAppearance.Signal.Body1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
|
@ -30,29 +42,45 @@
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textAppearance="@style/Signal.Text.Preview"
|
android:textAppearance="@style/Signal.Text.Preview"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/call_participant_video_muted"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/recipient_view_avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/full_names" />
|
tools:text="@tools:sample/full_names" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/call_participant_video_muted"
|
android:id="@+id/call_participant_video_muted"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/call_participant_audio_muted"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/recipient_view_name"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/ic_video_off_solid_white_18"
|
app:srcCompat="@drawable/ic_video_off_solid_white_18"
|
||||||
app:tint="@color/core_white"/>
|
app:tint="@color/core_white" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/call_participant_audio_muted"
|
android:id="@+id/call_participant_audio_muted"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/call_participant_screen_sharing"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/call_participant_video_muted"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/ic_mic_off_solid_18"
|
app:srcCompat="@drawable/ic_mic_off_solid_18"
|
||||||
app:tint="@color/core_white"/>
|
app:tint="@color/core_white" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/call_participant_screen_sharing"
|
android:id="@+id/call_participant_screen_sharing"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/call_participant_audio_muted"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/ic_share_screen_20"
|
app:srcCompat="@drawable/ic_share_screen_20"
|
||||||
app:tint="@color/core_white"/>
|
app:tint="@color/core_white" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -54,6 +54,15 @@
|
||||||
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
|
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
|
||||||
app:layout_constraintTop_toTopOf="@id/toolbar_icon" />
|
app:layout_constraintTop_toTopOf="@id/toolbar_icon" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/toolbar_settings_touch_area"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/toolbar_icon"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/unread_payments_indicator"
|
android:id="@+id/unread_payments_indicator"
|
||||||
android:layout_width="13dp"
|
android:layout_width="13dp"
|
||||||
|
|
|
@ -24,6 +24,18 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/typing_badge_1"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:badge_size="small"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/typing_avatar_1"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/typing_avatar_1"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
android:id="@+id/typing_avatar_2"
|
android:id="@+id/typing_avatar_2"
|
||||||
android:layout_width="@dimen/conversation_item_avatar_size"
|
android:layout_width="@dimen/conversation_item_avatar_size"
|
||||||
|
@ -39,6 +51,18 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/typing_badge_2"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:badge_size="small"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/typing_avatar_2"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/typing_avatar_2"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
android:id="@+id/typing_avatar_3"
|
android:id="@+id/typing_avatar_3"
|
||||||
android:layout_width="@dimen/conversation_item_avatar_size"
|
android:layout_width="@dimen/conversation_item_avatar_size"
|
||||||
|
@ -53,6 +77,17 @@
|
||||||
app:layout_constraintStart_toStartOf="@id/typing_avatar_2"
|
app:layout_constraintStart_toStartOf="@id/typing_avatar_2"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/typing_badge_3"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:badge_size="small"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/typing_avatar_3"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/typing_avatar_3" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/typing_count"
|
android:id="@+id/typing_count"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||||
|
android:background="@drawable/dsl_preference_item_background"
|
||||||
|
android:minHeight="56dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:srcCompat="@drawable/ic_advanced_24" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textAppearance="@style/Signal.Text.Body"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/summary"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/switcher"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
app:layout_goneMarginBottom="16dp"
|
||||||
|
app:layout_goneMarginStart="@dimen/dsl_settings_gutter"
|
||||||
|
tools:text="Message font size" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/summary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:lineSpacingExtra="4sp"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
android:textColor="@color/text_color_secondary_enabled_selector"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/switcher"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/icon"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title"
|
||||||
|
app:layout_goneMarginStart="@dimen/dsl_settings_gutter"
|
||||||
|
app:layout_goneMarginTop="16dp"
|
||||||
|
tools:text="Some random text to get stuff onto more than one line but not absurdly long like lorem/random"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ViewSwitcher
|
||||||
|
android:id="@+id/switcher"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/switch_widget"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:clickable="false" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/switch_progress"
|
||||||
|
style="@style/Widget.MaterialComponents.CircularProgressIndicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</ViewSwitcher>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -16,11 +16,25 @@
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
app:fallbackImageSize="small"
|
app:fallbackImageSize="small"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/recipient_view_name"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/recipient_view_badge"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="23dp"
|
||||||
|
app:badge_size="small"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/recipient_view_avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/recipient_view_avatar" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/recipient_view_name"
|
android:id="@+id/recipient_view_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
|
@ -28,6 +42,10 @@
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textAppearance="@style/Signal.Text.Preview"
|
android:textAppearance="@style/Signal.Text.Preview"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/recipient_view_avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/full_names" />
|
tools:text="@tools:sample/full_names" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -19,6 +19,18 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
|
android:id="@+id/recipient_view_badge"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="22dp"
|
||||||
|
android:contentDescription="@string/ImageView__badge"
|
||||||
|
app:badge_size="medium"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/recipient_view_avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/recipient_view_avatar"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/payments_home_payment_item_avatar_progress_overlay"
|
android:id="@+id/payments_home_payment_item_avatar_progress_overlay"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.badges.BadgeImageView
|
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||||
android:id="@+id/badge"
|
android:id="@+id/badge"
|
||||||
android:layout_width="200dp"
|
android:layout_width="160dp"
|
||||||
android:layout_height="200dp"
|
android:layout_height="160dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:contentDescription="@string/BadgesOverviewFragment__featured_badge"
|
android:contentDescription="@string/BadgesOverviewFragment__featured_badge"
|
||||||
app:badge_size="xlarge"
|
app:badge_size="xlarge"
|
||||||
|
|
|
@ -3999,6 +3999,7 @@
|
||||||
<string name="ExpiredBadgeBottomSheetDialogFragment__renew_subscription">Renew subscription</string>
|
<string name="ExpiredBadgeBottomSheetDialogFragment__renew_subscription">Renew subscription</string>
|
||||||
|
|
||||||
<string name="Subscription__verification_failed">Subscription Verification Failed</string>
|
<string name="Subscription__verification_failed">Subscription Verification Failed</string>
|
||||||
|
<string name="Subscription__redemption_failed">Badge Redemption Failed</string>
|
||||||
<string name="Subscription__please_contact_support_for_more_information">Please contact support for more information.</string>
|
<string name="Subscription__please_contact_support_for_more_information">Please contact support for more information.</string>
|
||||||
<string name="Subscription__contact_support">Contact Support</string>
|
<string name="Subscription__contact_support">Contact Support</string>
|
||||||
<string name="Subscription__earn_a_s_badge">Earn a %1$s badge</string>
|
<string name="Subscription__earn_a_s_badge">Earn a %1$s badge</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue