Implement several pieces of badge feedback.

fork-5.53.8
Alex Hart 2021-11-08 13:01:09 -04:00
rodzic 3d45ab1b36
commit 48e47c9d92
15 zmienionych plików z 370 dodań i 297 usunięć

Wyświetl plik

@ -399,7 +399,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".components.settings.conversation.ConversationSettingsActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:configChanges="touchscreen|keyboard|keyboardHidden|screenLayout|screenSize"
android:theme="@style/Signal.DayNight.ConversationSettings"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>

Wyświetl plik

@ -7,6 +7,7 @@ import com.google.android.flexbox.AlignItems
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.Badge
@ -36,8 +37,15 @@ object Badges {
}
.forEach { customPref(it) }
val perRow = context.resources.getInteger(R.integer.badge_columns)
val empties = (perRow - (badges.size % perRow)) % perRow
val gutter = context.resources.getDimensionPixelSize(R.dimen.dsl_settings_gutter)
val buffer = DimensionUnit.DP.toPixels(12f)
val gutterExtra = gutter - buffer
val badgeSize = DimensionUnit.DP.toPixels(88f)
val windowWidth = context.resources.displayMetrics.widthPixels
val availableWidth = windowWidth - gutterExtra
val perRow = (availableWidth / badgeSize).toInt()
val empties = ((perRow - (badges.size % perRow)) % perRow)
repeat(empties) {
customPref(Badge.EmptyModel())
}

Wyświetl plik

@ -3,49 +3,33 @@ package org.thoughtcrime.securesms.components.settings.app
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.navigation.NavDirections
import io.reactivex.rxjava3.subjects.PublishSubject
import io.reactivex.rxjava3.subjects.Subject
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.DSLSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.boost.BoostRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.boost.BoostViewModel
import org.thoughtcrime.securesms.components.settings.app.subscription.subscribe.SubscribeViewModel
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyvalue.SettingsValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.CachedInflater
import org.thoughtcrime.securesms.util.DynamicTheme
import org.thoughtcrime.securesms.util.FeatureFlags
private const val START_LOCATION = "app.settings.start.location"
private const val NOTIFICATION_CATEGORY = "android.intent.category.NOTIFICATION_PREFERENCES"
private const val STATE_WAS_CONFIGURATION_UPDATED = "app.settings.state.configuration.updated"
class AppSettingsActivity : DSLSettingsActivity() {
class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
private var wasConfigurationUpdated = false
private val donationRepository: DonationPaymentRepository by lazy { DonationPaymentRepository(this) }
private val subscribeViewModel: SubscribeViewModel by viewModels(
factoryProducer = {
SubscribeViewModel.Factory(SubscriptionsRepository(ApplicationDependencies.getDonationsService()), donationRepository, FETCH_SUBSCRIPTION_TOKEN_REQUEST_CODE)
}
)
private val boostViewModel: BoostViewModel by viewModels(
factoryProducer = {
BoostViewModel.Factory(BoostRepository(ApplicationDependencies.getDonationsService()), donationRepository, FETCH_BOOST_TOKEN_REQUEST_CODE)
}
)
override val donationPaymentRepository: DonationPaymentRepository by lazy { DonationPaymentRepository(this) }
override val googlePayResultPublisher: Subject<DonationPaymentComponent.GooglePayResult> = PublishSubject.create()
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
warmDonationViewModels()
if (intent?.hasExtra(ARG_NAV_GRAPH) != true) {
intent?.putExtra(ARG_NAV_GRAPH, R.navigation.app_settings)
}
@ -106,15 +90,10 @@ class AppSettingsActivity : DSLSettingsActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
subscribeViewModel.onActivityResult(requestCode, resultCode, data)
boostViewModel.onActivityResult(requestCode, resultCode, data)
googlePayResultPublisher.onNext(DonationPaymentComponent.GooglePayResult(requestCode, resultCode, data))
}
companion object {
private const val FETCH_SUBSCRIPTION_TOKEN_REQUEST_CODE = 1000
private const val FETCH_BOOST_TOKEN_REQUEST_CODE = 2000
@JvmStatic
fun home(context: Context): Intent = getIntentForStartLocation(context, StartLocation.HOME)
@ -149,13 +128,6 @@ class AppSettingsActivity : DSLSettingsActivity() {
}
}
private fun warmDonationViewModels() {
if (FeatureFlags.donorBadges()) {
subscribeViewModel
boostViewModel
}
}
private enum class StartLocation(val code: Int) {
HOME(0),
BACKUPS(1),

Wyświetl plik

@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.components.settings.app.subscription
import android.content.Intent
import io.reactivex.rxjava3.subjects.Subject
interface DonationPaymentComponent {
val donationPaymentRepository: DonationPaymentRepository
val googlePayResultPublisher: Subject<GooglePayResult>
class GooglePayResult(val requestCode: Int, val resultCode: Int, val data: Intent?)
}

Wyświetl plik

@ -1,9 +1,14 @@
package org.thoughtcrime.securesms.components.settings.app.subscription
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import com.google.android.gms.wallet.PaymentData
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log
@ -13,6 +18,7 @@ import org.signal.donations.GooglePayPaymentSource
import org.signal.donations.StripeApi
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.JobTracker
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@ -48,11 +54,25 @@ import java.util.concurrent.TimeUnit
*/
class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFetcher, StripeApi.SetupIntentHelper {
private val application = activity.application
private val googlePayApi = GooglePayApi(activity, StripeApi.Gateway(Environment.Donations.STRIPE_CONFIGURATION), Environment.Donations.GOOGLE_PAY_CONFIGURATION)
private val stripeApi = StripeApi(Environment.Donations.STRIPE_CONFIGURATION, this, this, ApplicationDependencies.getOkHttpClient())
fun isGooglePayAvailable(): Completable = googlePayApi.queryIsReadyToPay()
fun internetConnectionObserver(): Observable<Boolean> = Observable.create {
val observer = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (!it.isDisposed) {
it.onNext(NetworkConstraint.isMet(application))
}
}
}
it.setCancellable { application.unregisterReceiver(observer) }
application.registerReceiver(observer, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
fun requestTokenFromGooglePay(price: FiatMoney, label: String, requestCode: Int) {
googlePayApi.requestPayment(price, label, requestCode)
}

Wyświetl plik

@ -24,11 +24,14 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationExceptions
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.components.settings.models.Progress
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.util.BottomSheetUtil.requireCoordinatorLayout
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.LifecycleDisposable
@ -42,7 +45,12 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
layoutId = R.layout.boost_bottom_sheet
) {
private val viewModel: BoostViewModel by viewModels(ownerProducer = { requireActivity() })
private val viewModel: BoostViewModel by viewModels(
factoryProducer = {
BoostViewModel.Factory(BoostRepository(ApplicationDependencies.getDonationsService()), donationPaymentComponent.donationPaymentRepository, FETCH_BOOST_TOKEN_REQUEST_CODE)
}
)
private val lifecycleDisposable = LifecycleDisposable()
private lateinit var boost1AnimationView: LottieAnimationView
@ -53,6 +61,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
private lateinit var boost6AnimationView: LottieAnimationView
private lateinit var processingDonationPaymentDialog: AlertDialog
private lateinit var donationPaymentComponent: DonationPaymentComponent
private val sayThanks: CharSequence by lazy {
SpannableStringBuilder(requireContext().getString(R.string.BoostFragment__say_thanks_and_earn, 30))
@ -65,6 +74,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
}
override fun bindAdapter(adapter: DSLSettingsAdapter) {
donationPaymentComponent = findListener()!!
viewModel.refresh()
CurrencySelection.register(adapter)
@ -277,5 +287,6 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
companion object {
private val TAG = Log.tag(BoostFragment::class.java)
private const val FETCH_BOOST_TOKEN_REQUEST_CODE = 2000
}
}

Wyświetl plik

@ -8,6 +8,7 @@ import com.google.android.gms.wallet.PaymentData
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.subjects.PublishSubject
@ -32,14 +33,28 @@ class BoostViewModel(
private val store = Store(BoostState(currencySelection = SignalStore.donationsValues().getBoostCurrency()))
private val eventPublisher: PublishSubject<DonationEvent> = PublishSubject.create()
private val disposables = CompositeDisposable()
private val networkDisposable: Disposable
val state: LiveData<BoostState> = store.stateLiveData
val events: Observable<DonationEvent> = eventPublisher.observeOn(AndroidSchedulers.mainThread())
private var boostToPurchase: Boost? = null
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
.distinctUntilChanged()
.subscribe { isConnected ->
if (!disposables.isDisposed && isConnected && store.state.stage == BoostState.Stage.FAILURE) {
store.update { it.copy(stage = BoostState.Stage.INIT) }
refresh()
}
}
}
override fun onCleared() {
disposables.clear()
networkDisposable.dispose()
disposables.dispose()
}
fun getSupportedCurrencyCodes(): List<String> {

Wyświetl plik

@ -59,7 +59,7 @@ object ActiveSubscriptionPreference {
expiry.text = context.getString(
R.string.MySupportPreference__renews_s,
DateUtils.formatDate(
DateUtils.formatDateWithYear(
Locale.getDefault(),
model.renewalTimestamp
)

Wyświetl plik

@ -21,11 +21,15 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationExceptions
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.components.settings.models.Progress
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.CommunicationActions
@ -41,8 +45,6 @@ class SubscribeFragment : DSLSettingsFragment(
layoutId = R.layout.subscribe_fragment
) {
private val viewModel: SubscribeViewModel by viewModels(ownerProducer = { requireActivity() })
private val lifecycleDisposable = LifecycleDisposable()
private val supportTechSummary: CharSequence by lazy {
@ -56,6 +58,13 @@ class SubscribeFragment : DSLSettingsFragment(
}
private lateinit var processingDonationPaymentDialog: AlertDialog
private lateinit var donationPaymentComponent: DonationPaymentComponent
private val viewModel: SubscribeViewModel by viewModels(
factoryProducer = {
SubscribeViewModel.Factory(SubscriptionsRepository(ApplicationDependencies.getDonationsService()), donationPaymentComponent.donationPaymentRepository, FETCH_SUBSCRIPTION_TOKEN_REQUEST_CODE)
}
)
override fun onResume() {
super.onResume()
@ -63,6 +72,7 @@ class SubscribeFragment : DSLSettingsFragment(
}
override fun bindAdapter(adapter: DSLSettingsAdapter) {
donationPaymentComponent = findListener()!!
viewModel.refresh()
BadgePreview.register(adapter)
@ -92,6 +102,9 @@ class SubscribeFragment : DSLSettingsFragment(
is DonationEvent.SubscriptionCancellationFailed -> onSubscriptionFailedToCancel(it.throwable)
}
}
lifecycleDisposable += donationPaymentComponent.googlePayResultPublisher.subscribe {
viewModel.onActivityResult(it.requestCode, it.resultCode, it.data)
}
}
private fun getConfiguration(state: SubscribeState): DSLConfiguration {
@ -302,5 +315,6 @@ class SubscribeFragment : DSLSettingsFragment(
companion object {
private val TAG = Log.tag(SubscribeFragment::class.java)
private const val FETCH_SUBSCRIPTION_TOKEN_REQUEST_CODE = 1000
}
}

Wyświetl plik

@ -9,6 +9,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.subjects.PublishSubject
@ -38,6 +39,7 @@ class SubscribeViewModel(
private val store = Store(SubscribeState(currencySelection = SignalStore.donationsValues().getSubscriptionCurrency()))
private val eventPublisher: PublishSubject<DonationEvent> = PublishSubject.create()
private val disposables = CompositeDisposable()
private val networkDisposable: Disposable
val state: LiveData<SubscribeState> = store.stateLiveData
val events: Observable<DonationEvent> = eventPublisher.observeOn(AndroidSchedulers.mainThread())
@ -45,8 +47,21 @@ class SubscribeViewModel(
private var subscriptionToPurchase: Subscription? = null
private val activeSubscriptionSubject = PublishSubject.create<ActiveSubscription>()
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
.distinctUntilChanged()
.subscribe { isConnected ->
if (!disposables.isDisposed && isConnected && store.state.stage == SubscribeState.Stage.FAILURE) {
store.update { it.copy(stage = SubscribeState.Stage.INIT) }
refresh()
}
}
}
override fun onCleared() {
disposables.clear()
networkDisposable.dispose()
disposables.dispose()
}
fun getPriceOfSelectedSubscription(): FiatMoney? {

Wyświetl plik

@ -83,6 +83,7 @@ import org.thoughtcrime.securesms.MainNavigator;
import org.thoughtcrime.securesms.MuteDialog;
import org.thoughtcrime.securesms.NewConversationActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.badges.BadgeImageView;
import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.badges.self.expired.ExpiredBadgeBottomSheetDialogFragment;
import org.thoughtcrime.securesms.components.RatingManager;
@ -519,6 +520,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private void initializeProfileIcon(@NonNull Recipient recipient) {
ImageView icon = requireView().findViewById(R.id.toolbar_icon);
BadgeImageView imageView = requireView().findViewById(R.id.toolbar_badge);
imageView.setBadgeFromRecipient(recipient);
AvatarUtil.loadIconIntoImageView(recipient, icon, getResources().getDimensionPixelSize(R.dimen.toolbar_avatar_size));
icon.setOnClickListener(v -> getNavigator().goToAppSettings());
}

Wyświetl plik

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:tools="http://schemas.android.com/tools"
android:id="@+id/constraint_layout"
@ -9,261 +8,271 @@
android:layout_height="match_parent"
android:background="?android:windowBackground">
<ViewStub
android:id="@+id/voice_note_player"
android:inflatedId="@+id/voice_note_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/voice_note_player_stub"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/signal_background_primary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarStyle"
android:visibility="gone"
app:contentInsetStart="0dp"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/toolbar_icon"
android:layout_width="@dimen/toolbar_avatar_size"
android:layout_height="@dimen/toolbar_avatar_size"
android:layout_alignParentStart="true"
android:contentDescription="@string/conversation_list_settings_shortcut"
android:layout_marginStart="@dimen/toolbar_avatar_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_contact_picture" />
<View
android:id="@+id/unread_payments_indicator"
android:layout_width="13dp"
android:layout_height="13dp"
android:background="@drawable/unread_count_background"
android:alpha="0"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toBottomOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
tools:alpha="1" />
<TextView
android:id="@+id/conversation_list_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:text="@string/app_name"
android:textAlignment="viewStart"
android:textColor="@color/signal_text_primary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_proxy_status"
app:layout_constraintStart_toEndOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/conversation_list_proxy_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
android:background="?selectableItemBackgroundBorderless"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@id/conversation_list_title"
app:layout_constraintEnd_toStartOf="@id/search_action"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:src="@drawable/ic_proxy_connected_24"
tools:visibility="visible"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/search_action"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?actionBarItemBackground"
android:contentDescription="@string/conversation_list_search_description"
android:padding="12dp"
android:tint="@color/signal_icon_tint_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_search_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.thoughtcrime.securesms.util.views.DarkOverflowToolbar>
<ViewStub
android:id="@+id/toolbar_basic"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout="@layout/conversation_list_archive_toolbar"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/search_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_search_toolbar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/toolbar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="toolbar,toolbar_basic" />
<View
android:id="@+id/conversation_list_toolbar_shadow"
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@drawable/toolbar_shadow"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
tools:visibility="visible" />
<TextView
android:id="@+id/search_no_results"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/signal_background_primary"
android:gravity="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
tools:text="@string/SearchFragment_no_results" />
<ViewStub
android:id="@+id/empty_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_empty_state"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<ViewStub
android:id="@+id/reminder"
android:inflatedId="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_reminder_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<ViewStub
android:id="@+id/payments_notification"
android:inflatedId="@+id/payments_notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/payment_notification_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/banner_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="reminder,payments_notification,voice_note_player" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:nextFocusDown="@+id/fab"
android:nextFocusForward="@+id/fab"
android:paddingBottom="160dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner_barrier"
tools:listitem="@layout/conversation_list_item_view"
tools:visibility="visible" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner_barrier"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:clipChildren="false">
<LinearLayout
<ViewStub
android:id="@+id/voice_note_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|end"
android:gravity="end"
android:inflatedId="@+id/voice_note_player"
android:layout="@layout/voice_note_player_stub"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/signal_background_primary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarStyle"
android:visibility="gone"
app:contentInsetStart="0dp"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/toolbar_icon"
android:layout_width="@dimen/toolbar_avatar_size"
android:layout_height="@dimen/toolbar_avatar_size"
android:layout_alignParentStart="true"
android:layout_marginStart="@dimen/toolbar_avatar_margin"
android:contentDescription="@string/conversation_list_settings_shortcut"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_contact_picture" />
<org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/toolbar_badge"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="14dp"
android:layout_marginTop="16dp"
app:badge_size="small"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="@id/toolbar_icon" />
<View
android:id="@+id/unread_payments_indicator"
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:alpha="0"
android:background="@drawable/unread_count_background"
app:layout_constraintBottom_toBottomOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
tools:alpha="1" />
<TextView
android:id="@+id/conversation_list_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:text="@string/app_name"
android:textAlignment="viewStart"
android:textColor="@color/signal_text_primary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_proxy_status"
app:layout_constraintStart_toEndOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/conversation_list_proxy_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:padding="12dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/search_action"
app:layout_constraintStart_toEndOf="@id/conversation_list_title"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_proxy_connected_24"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/search_action"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?actionBarItemBackground"
android:contentDescription="@string/conversation_list_search_description"
android:padding="12dp"
android:tint="@color/signal_icon_tint_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_search_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.thoughtcrime.securesms.util.views.DarkOverflowToolbar>
<ViewStub
android:id="@+id/toolbar_basic"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout="@layout/conversation_list_archive_toolbar"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/search_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_search_toolbar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/toolbar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="toolbar,toolbar_basic" />
<View
android:id="@+id/conversation_list_toolbar_shadow"
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@drawable/toolbar_shadow"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
tools:visibility="visible" />
<TextView
android:id="@+id/search_no_results"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/signal_background_primary"
android:gravity="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
tools:text="@string/SearchFragment_no_results" />
<ViewStub
android:id="@+id/empty_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout="@layout/conversation_list_empty_state"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<ViewStub
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_list_reminder_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<ViewStub
android:id="@+id/payments_notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/payments_notification"
android:layout="@layout/payment_notification_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/banner_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="reminder,payments_notification,voice_note_player" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:nextFocusDown="@+id/fab"
android:nextFocusForward="@+id/fab"
android:paddingBottom="160dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner_barrier"
tools:listitem="@layout/conversation_list_item_view"
tools:visibility="visible" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipChildren="false"
android:clipToPadding="false"
app:layout_behavior="org.thoughtcrime.securesms.util.views.SlideUpWithSnackbarBehavior">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner_barrier">
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/camera_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:contentDescription="@string/conversation_list_fragment__open_camera_description"
android:focusable="true"
android:tint="@color/signal_icon_tint_secondary"
app:backgroundTint="@color/conversation_list_camera_button_background"
app:srcCompat="@drawable/ic_camera_solid_white_24" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="end"
android:orientation="vertical"
app:layout_behavior="org.thoughtcrime.securesms.util.views.SlideUpWithSnackbarBehavior">
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:contentDescription="@string/conversation_list_fragment__fab_content_description"
android:focusable="true"
android:tint="@color/core_white"
app:srcCompat="@drawable/ic_compose_solid_24"
app:backgroundTint="@color/core_ultramarine"/>
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/camera_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp"
android:contentDescription="@string/conversation_list_fragment__open_camera_description"
android:focusable="true"
android:tint="@color/signal_icon_tint_secondary"
app:backgroundTint="@color/conversation_list_camera_button_background"
app:srcCompat="@drawable/ic_camera_solid_white_24" />
<ViewStub
android:id="@+id/megaphone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_megaphone_container"
app:layout_constraintBottom_toBottomOf="parent" />
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:contentDescription="@string/conversation_list_fragment__fab_content_description"
android:focusable="true"
android:tint="@color/core_white"
app:backgroundTint="@color/core_ultramarine"
app:srcCompat="@drawable/ic_compose_solid_24" />
</LinearLayout>
<ViewStub
android:id="@+id/megaphone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_megaphone_container"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
<org.thoughtcrime.securesms.components.menu.SignalBottomActionBar
android:id="@+id/conversation_list_bottom_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="36dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<org.thoughtcrime.securesms.components.menu.SignalBottomActionBar
android:id="@+id/conversation_list_bottom_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="36dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -32,6 +32,4 @@
<dimen name="toolbar_avatar_margin">34dp</dimen>
<dimen name="verify_identity_vertical_margin">32dp</dimen>
<integer name="badge_columns">4</integer>
</resources>

Wyświetl plik

@ -2,6 +2,4 @@
<resources>
<dimen name="media_bubble_max_width">350dp</dimen>
<dimen name="media_bubble_max_height">300dp</dimen>
<integer name="badge_columns">5</integer>
</resources>

Wyświetl plik

@ -210,7 +210,5 @@
<dimen name="verify_identity_vertical_margin">16dp</dimen>
<integer name="badge_columns">3</integer>
<dimen name="signal_context_menu_corner_radius">18dp</dimen>
</resources>