Merge pull request #8426 from vector-im/feature/bma/a11yFixes

A11y fixes
This commit is contained in:
Benoit Marty 2023-05-15 16:28:35 +02:00 committed by GitHub
commit 7c3ecec92a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 242 additions and 55 deletions

1
changelog.d/8426.misc Normal file
View file

@ -0,0 +1 @@
Improve keyboard navigation and accessibility when using a screen reader.

View file

@ -1470,6 +1470,9 @@
<string name="reason_colon">Reason: %1$s</string>
<string name="avatar">Avatar</string>
<string name="avatar_of_space">Avatar of space %1$s</string>
<string name="avatar_of_room">Avatar of room %1$s</string>
<string name="avatar_of_user">Profile picture of user %1$s</string>
<!-- Consent modal -->
<string name="dialog_user_consent_content">To continue using the %1$s homeserver you must review and agree to the terms and conditions.</string>

View file

@ -4,6 +4,9 @@
<style name="Widget.Vector.AppBarLayout" parent="Widget.MaterialComponents.AppBarLayout.Primary">
<item name="android:background">?vctr_toolbar_background</item>
<item name="elevation">4dp</item>
<!-- a11y -->
<item name="android:touchscreenBlocksFocus">false</item>
</style>
</resources>

View file

@ -12,6 +12,9 @@
<item name="subtitleTextAppearance">@style/TextAppearance.Vector.Widget.ActionBarSubTitle</item>
<item name="navigationIconTint">?vctr_content_secondary</item>
<!-- a11y -->
<item name="android:touchscreenBlocksFocus">false</item>
</style>
<!-- Default toolbar style -->
@ -39,14 +42,24 @@
<item name="android:textSize">12sp</item>
</style>
<!-- Material 3 -->
<!-- CollapsingToolbar -->
<style name="Widget.Vector.Material3.Toolbar" parent="Widget.Material3.Toolbar" />
<style name="Widget.Vector.CollapsingToolbar" parent="Widget.Material3.CollapsingToolbar">
<!-- a11y -->
<item name="android:touchscreenBlocksFocus">false</item>
</style>
<style name="Widget.Vector.Material3.CollapsingToolbar.Medium" parent="Widget.Material3.CollapsingToolbar.Medium">
<style name="Widget.Vector.CollapsingToolbar.Medium" parent="Widget.Material3.CollapsingToolbar.Medium">
<item name="expandedTitleTextAppearance">@style/TextAppearance.Vector.Title.Medium</item>
<item name="expandedTitleMarginBottom">20dp</item>
<item name="collapsedTitleTextAppearance">@style/TextAppearance.Vector.Headline.Bold</item>
<!-- a11y -->
<item name="android:touchscreenBlocksFocus">false</item>
</style>
<style name="Widget.Vector.CollapsingToolbar.Large" parent="Widget.Material3.CollapsingToolbar.Large">
<!-- a11y -->
<item name="android:touchscreenBlocksFocus">false</item>
</style>
</resources>

View file

@ -83,6 +83,9 @@
<item name="android:textViewStyle">@style/Widget.Vector.TextView.Body</item>
<item name="materialButtonStyle">@style/Widget.Vector.Button</item>
<item name="toolbarStyle">@style/Widget.Vector.Toolbar</item>
<item name="collapsingToolbarLayoutStyle">@style/Widget.Vector.CollapsingToolbar</item>
<item name="collapsingToolbarLayoutMediumStyle">@style/Widget.Vector.CollapsingToolbar.Medium</item>
<item name="collapsingToolbarLayoutLargeStyle">@style/Widget.Vector.CollapsingToolbar.Large</item>
<item name="bottomNavigationStyle">@style/BottomNavigation.Vector</item>
<item name="searchViewStyle">@style/Widget.Vector.SearchView</item>
<item name="textInputStyle">@style/Widget.Vector.TextInputLayout</item>

View file

@ -83,6 +83,9 @@
<item name="android:textViewStyle">@style/Widget.Vector.TextView.Body</item>
<item name="materialButtonStyle">@style/Widget.Vector.Button</item>
<item name="toolbarStyle">@style/Widget.Vector.Toolbar</item>
<item name="collapsingToolbarLayoutStyle">@style/Widget.Vector.CollapsingToolbar</item>
<item name="collapsingToolbarLayoutMediumStyle">@style/Widget.Vector.CollapsingToolbar.Medium</item>
<item name="collapsingToolbarLayoutLargeStyle">@style/Widget.Vector.CollapsingToolbar.Large</item>
<item name="bottomNavigationStyle">@style/BottomNavigation.Vector</item>
<item name="searchViewStyle">@style/Widget.Vector.SearchView</item>
<item name="textInputStyle">@style/Widget.Vector.TextInputLayout</item>

View file

@ -20,6 +20,8 @@ import android.graphics.drawable.Drawable
import android.text.InputType
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.EditText
import android.widget.ImageView
import androidx.annotation.AttrRes
@ -28,6 +30,7 @@ import androidx.annotation.DrawableRes
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.transition.ChangeBounds
import androidx.transition.Fade
@ -97,6 +100,14 @@ fun View.setAttributeBackground(@AttrRes attributeId: Int) {
setBackgroundResource(attribute.resourceId)
}
/**
* Inspired from https://stackoverflow.com/a/64597532/1472514. Safer to call the 2 available API.
*/
fun View.giveAccessibilityFocus() {
ViewCompat.performAccessibilityAction(this, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED)
}
fun ViewGroup.animateLayoutChange(animationDuration: Long, transitionComplete: (() -> Unit)? = null) {
val transition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_SEQUENTIAL

View file

@ -45,6 +45,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActivityEntryPoint
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.giveAccessibilityFocus
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.utils.ToolbarConfig
@ -318,4 +319,19 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
.setPositiveButton(R.string.ok, null)
.show()
}
/* ==========================================================================================
* Accessibility - a11y
* ========================================================================================== */
private var hasBeenAccessibilityFocused = false
/**
* Ensure the View get the accessibility focus. This method has effect only once per fragment instance.
*/
protected fun View.giveAccessibilityFocusOnce() {
if (hasBeenAccessibilityFocused) return
hasBeenAccessibilityFocused = true
giveAccessibilityFocus()
}
}

View file

@ -60,5 +60,6 @@ class BootstrapConclusionFragment :
.toSpannable()
.colorizeMatchingText(getString(R.string.recovery_passphrase), colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
.colorizeMatchingText(getString(R.string.message_key), colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
views.bootstrapConclusionText.giveAccessibilityFocusOnce()
}
}

View file

@ -52,6 +52,7 @@ class BootstrapConfirmPassphraseFragment :
views.ssssPassphraseSecurityProgress.isGone = true
views.bootstrapDescriptionText.text = getString(R.string.set_a_security_phrase_again_notice)
views.bootstrapDescriptionText.giveAccessibilityFocusOnce()
views.ssssPassphraseEnterEdittext.hint = getString(R.string.set_a_security_phrase_hint)
withState(sharedViewModel) {

View file

@ -118,5 +118,6 @@ class BootstrapEnterPassphraseFragment :
}
}
}
views.bootstrapDescriptionText.giveAccessibilityFocusOnce()
}
}

View file

@ -141,6 +141,7 @@ class BootstrapMigrateBackupFragment :
views.bootstrapMigrateUseFile.isVisible = false
}
views.bootstrapDescriptionText.giveAccessibilityFocusOnce()
}
private val importFileStartForActivityResult = registerStartForActivityResult { activityResult ->

View file

@ -78,5 +78,6 @@ class BootstrapReAuthFragment :
views.bootstrapCancelButton.isVisible = true
views.bootstrapRetryButton.isVisible = true
}
views.bootstrapDescriptionText.giveAccessibilityFocusOnce()
}
}

View file

@ -117,5 +117,6 @@ class BootstrapSaveRecoveryKeyFragment :
views.recoveryContinue.isVisible = step.isSaved
views.bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
views.bootstrapSaveText.giveAccessibilityFocusOnce()
}
}

View file

@ -68,6 +68,7 @@ class BootstrapSetupRecoveryKeyFragment :
// Choose between create a passphrase or use a recovery key
renderBackupMethodActions(firstFormStep.methods)
}
views.bootstrapSetupSecureText.giveAccessibilityFocusOnce()
}
private fun renderStateWithExistingKeyBackup() = with(views) {

View file

@ -52,5 +52,6 @@ class BootstrapWaitingFragment :
views.bootstrapDescriptionText.isVisible = false
}
}
views.bootstrapDescriptionText.giveAccessibilityFocusOnce()
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification
import android.view.LayoutInflater
import android.view.ViewGroup
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentProgressBinding
@AndroidEntryPoint
class QuadSLoadingFragment :
VectorBaseFragment<FragmentProgressBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentProgressBinding {
return FragmentProgressBinding.inflate(inflater, container, false)
}
}

View file

@ -21,12 +21,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.giveAccessibilityFocus
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
@ -36,6 +42,7 @@ import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.qrcode.QrCodeScannerActivity
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import timber.log.Timber
import javax.inject.Inject
@ -45,6 +52,16 @@ class SelfVerificationFragment : VectorBaseFragment<BottomSheetVerificationChil
@Inject lateinit var controller: SelfVerificationController
private var requestAccessibilityFocus: Boolean = false
private val modelBuildListener: OnModelBuildFinishedListener = OnModelBuildFinishedListener {
if (requestAccessibilityFocus) {
// Do not use giveAccessibilityFocusOnce() here.
views.bottomSheetVerificationRecyclerView.layoutManager?.findViewByPosition(0)?.giveAccessibilityFocus()
requestAccessibilityFocus = false
// Note: it does not work when the recycler view is displayed for the first time, because findViewByPosition(0) is null
}
}
private val viewModel by parentFragmentViewModel(SelfVerificationViewModel::class)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
@ -58,17 +75,22 @@ class SelfVerificationFragment : VectorBaseFragment<BottomSheetVerificationChil
override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup()
controller.removeModelBuildListener(modelBuildListener)
controller.listener = null
super.onDestroyView()
}
private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.addModelBuildListener(modelBuildListener)
controller.listener = this
}
override fun invalidate() = withState(viewModel) { state ->
// Timber.w("VALR: invalidate with State: ${state.pendingRequest}")
if (state.isNewScreen()) {
requestAccessibilityFocus = true
}
controller.update(state)
}
@ -176,4 +198,41 @@ class SelfVerificationFragment : VectorBaseFragment<BottomSheetVerificationChil
override fun declineRequest() {
viewModel.handle(VerificationAction.CancelPendingVerification)
}
private var currentScreenIndex = -1
private fun SelfVerificationViewState.isNewScreen(): Boolean {
val newIndex = toScreenIndex()
if (currentScreenIndex == newIndex) {
return false
}
currentScreenIndex = newIndex
return true
}
private fun SelfVerificationViewState.toScreenIndex(): Int {
return if (activeAction !is UserAction.None) {
when (activeAction) {
UserAction.ConfirmCancel -> 30
UserAction.None -> 31
}
} else {
when (pendingRequest) {
is Fail -> 0
is Loading -> 1
is Success -> when (pendingRequest.invoke().state) {
EVerificationState.WaitingForReady -> 10
EVerificationState.Requested -> 11
EVerificationState.Ready -> 12
EVerificationState.Started -> 13
EVerificationState.WeStarted -> 14
EVerificationState.WaitingForDone -> 15
EVerificationState.Done -> 16
EVerificationState.Cancelled -> 17
EVerificationState.HandledByOtherSession -> 18
}
Uninitialized -> 2
}
}
}
}

View file

@ -21,12 +21,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.giveAccessibilityFocus
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
@ -36,6 +42,7 @@ import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.qrcode.QrCodeScannerActivity
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import timber.log.Timber
import javax.inject.Inject
@ -45,6 +52,16 @@ class UserVerificationFragment : VectorBaseFragment<BottomSheetVerificationChild
@Inject lateinit var controller: UserVerificationController
private var requestAccessibilityFocus: Boolean = false
private val modelBuildListener: OnModelBuildFinishedListener = OnModelBuildFinishedListener {
if (requestAccessibilityFocus) {
// Do not use giveAccessibilityFocusOnce() here.
views.bottomSheetVerificationRecyclerView.layoutManager?.findViewByPosition(0)?.giveAccessibilityFocus()
requestAccessibilityFocus = false
// Note: it does not work when the recycler view is displayed for the first time, because findViewByPosition(0) is null
}
}
private val viewModel by parentFragmentViewModel(UserVerificationViewModel::class)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
@ -58,17 +75,22 @@ class UserVerificationFragment : VectorBaseFragment<BottomSheetVerificationChild
override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup()
controller.removeModelBuildListener(modelBuildListener)
controller.listener = null
super.onDestroyView()
}
private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.addModelBuildListener(modelBuildListener)
controller.listener = this
}
override fun invalidate() = withState(viewModel) { state ->
// Timber.w("VALR: invalidate with State: ${state.pendingRequest}")
if (state.isNewScreen()) {
requestAccessibilityFocus = true
}
controller.update(state)
}
@ -148,4 +170,34 @@ class UserVerificationFragment : VectorBaseFragment<BottomSheetVerificationChild
override fun onUserConfirmsQrCodeScanned() {
viewModel.handle(VerificationAction.OtherUserScannedSuccessfully)
}
private var currentScreenIndex = -1
private fun UserVerificationViewState.isNewScreen(): Boolean {
val newIndex = toScreenIndex()
if (currentScreenIndex == newIndex) {
return false
}
currentScreenIndex = newIndex
return true
}
private fun UserVerificationViewState.toScreenIndex(): Int {
return when (pendingRequest) {
is Fail -> 0
is Loading -> 1
is Success -> when (pendingRequest.invoke().state) {
EVerificationState.WaitingForReady -> 10
EVerificationState.Requested -> 11
EVerificationState.Ready -> 12
EVerificationState.Started -> 13
EVerificationState.WeStarted -> 14
EVerificationState.WaitingForDone -> 15
EVerificationState.Done -> 16
EVerificationState.Cancelled -> 17
EVerificationState.HandledByOtherSession -> 18
}
Uninitialized -> 2
}
}
}

View file

@ -35,13 +35,16 @@ import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.DrawableImageViewTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.signature.ObjectKey
import im.vector.app.R
import im.vector.app.core.contacts.MappedContact
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.AvatarPlaceholder
import im.vector.app.core.glide.GlideApp
import im.vector.app.core.glide.GlideRequest
import im.vector.app.core.glide.GlideRequests
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import jp.wasabeef.glide.transformations.BlurTransformation
import jp.wasabeef.glide.transformations.ColorFilterTransformation
@ -58,7 +61,8 @@ import javax.inject.Inject
class AvatarRenderer @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val matrixItemColorProvider: MatrixItemColorProvider,
private val dimensionConverter: DimensionConverter
private val dimensionConverter: DimensionConverter,
private val stringProvider: StringProvider,
) {
companion object {
@ -67,6 +71,7 @@ class AvatarRenderer @Inject constructor(
@UiThread
fun render(matrixItem: MatrixItem, imageView: ImageView) {
imageView.setContentDescription(matrixItem)
render(
GlideApp.with(imageView),
matrixItem,
@ -100,6 +105,7 @@ class AvatarRenderer @Inject constructor(
@UiThread
fun render(matrixItem: MatrixItem, imageView: ImageView, glideRequests: GlideRequests) {
imageView.setContentDescription(matrixItem)
render(
glideRequests,
matrixItem,
@ -109,6 +115,7 @@ class AvatarRenderer @Inject constructor(
@UiThread
fun render(matrixItem: MatrixItem, localUri: Uri?, imageView: ImageView) {
imageView.setContentDescription(matrixItem)
val placeholder = getPlaceholderDrawable(matrixItem)
GlideApp.with(imageView)
.load(localUri?.let { File(localUri.path!!) })
@ -295,4 +302,28 @@ class AvatarRenderer @Inject constructor(
return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver()
?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)
}
/**
* Accessibility management.
*/
private fun ImageView.setContentDescription(matrixItem: MatrixItem) {
// Do not set contentDescription if the ImageView should be ignored regarding accessibility.
if (isImportantForAccessibility.not()) return
when (matrixItem) {
is MatrixItem.SpaceItem -> {
contentDescription = stringProvider.getString(R.string.avatar_of_space, matrixItem.getBestName())
}
is MatrixItem.RoomAliasItem,
is MatrixItem.RoomItem -> {
contentDescription = stringProvider.getString(R.string.avatar_of_room, matrixItem.getBestName())
}
is MatrixItem.UserItem -> {
contentDescription = stringProvider.getString(R.string.avatar_of_user, matrixItem.getBestName())
}
is MatrixItem.EveryoneInRoomItem,
is MatrixItem.EventItem -> {
// NA
}
}
}
}

View file

@ -21,9 +21,12 @@ import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
import androidx.core.view.ViewCompat
import com.tapadoo.alerter.Alerter
import im.vector.app.R
import im.vector.app.core.extensions.giveAccessibilityFocus
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.isAnimationEnabled
import im.vector.app.features.MainActivity
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
@ -46,6 +49,7 @@ import javax.inject.Singleton
@Singleton
class PopupAlertManager @Inject constructor(
private val clock: Clock,
private val stringProvider: StringProvider,
) {
companion object {
@ -282,6 +286,9 @@ class PopupAlertManager @Inject constructor(
}
currentIsDismissed()
}
.setOnShowListener {
handleAccessibility(activity, animate)
}
.enableSwipeToDismiss()
.enableInfiniteDuration(true)
.apply {
@ -297,6 +304,29 @@ class PopupAlertManager @Inject constructor(
.show()
}
/* a11y */
private fun handleAccessibility(activity: Activity, giveFocus: Boolean) {
activity.window.decorView.findViewById<View>(R.id.llAlertBackground)?.let { alertView ->
alertView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
// Add close action for a11y (same action than swipe). User can select the action by swiping on the screen vertically,
// and double tap to perform the action
ViewCompat.addAccessibilityAction(
alertView,
stringProvider.getString(R.string.action_close)
) { _, _ ->
currentIsDismissed()
Alerter.hide()
true
}
// And give focus to the alert right now, only for first display, i.e. when there is an animation.
if (giveFocus) {
alertView.giveAccessibilityFocus()
}
}
}
private fun currentIsDismissed() {
// current alert has been hidden
if (currentAlerter?.isLight == false) {

View file

@ -49,15 +49,14 @@
app:layout_constraintTop_toBottomOf="@id/syncStateView">
<com.google.android.material.appbar.CollapsingToolbarLayout
style="?attr/collapsingToolbarLayoutMediumStyle"
android:id="@+id/collapsing_toolbar"
style="@style/Widget.Vector.Material3.CollapsingToolbar.Medium"
android:layout_width="match_parent"
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Widget.Vector.Material3.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="0dp"

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:indeterminate="true" />
</RelativeLayout>