fix: Improve accessibility of ComposeActivity bottomsheets and buttons (#548)
The previous bottomsheets did set a minimum height for the menu items, so they were less than the recommended 48dp minimum. Fix that to improve the overall accessibility. Always highlight the "visibilty" icon, to make it clear that it's something that is set (even if to the default). Show the visibility icon on the "Toot" button as an additional reminder to the user. Other changes: - Use the "filled" style for all icons (the visibility icons had the "outlined" style) - Use the `makeIcon` helper function. - Use the `Status.Visibility` extension functions to determine the icon for each visibility type, reducing code duplication.
This commit is contained in:
parent
0ad25bd617
commit
8ef227fd20
|
@ -1748,7 +1748,7 @@
|
|||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/activity_compose.xml"
|
||||
line="364"
|
||||
line="368"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
|
|
|
@ -99,6 +99,8 @@ import app.pachli.util.getInitialLanguages
|
|||
import app.pachli.util.getLocaleList
|
||||
import app.pachli.util.getMediaSize
|
||||
import app.pachli.util.highlightSpans
|
||||
import app.pachli.util.iconRes
|
||||
import app.pachli.util.makeIcon
|
||||
import app.pachli.util.modernLanguageCode
|
||||
import app.pachli.util.setDrawableTint
|
||||
import com.canhub.cropper.CropImage
|
||||
|
@ -108,8 +110,8 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.IconicsSize
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.io.File
|
||||
|
@ -515,24 +517,13 @@ class ComposeActivity :
|
|||
displayTransientMessage(R.string.hint_media_description_missing)
|
||||
}
|
||||
|
||||
val textColor = MaterialColors.getColor(binding.root, android.R.attr.textColorTertiary)
|
||||
|
||||
val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply {
|
||||
colorInt = textColor
|
||||
sizeDp = 18
|
||||
}
|
||||
val cameraIcon = makeIcon(this, GoogleMaterial.Icon.gmd_camera_alt, IconicsSize.dp(18))
|
||||
binding.actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null)
|
||||
|
||||
val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply {
|
||||
colorInt = textColor
|
||||
sizeDp = 18
|
||||
}
|
||||
val imageIcon = makeIcon(this, GoogleMaterial.Icon.gmd_image, IconicsSize.dp(18))
|
||||
binding.actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null)
|
||||
|
||||
val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply {
|
||||
colorInt = textColor
|
||||
sizeDp = 18
|
||||
}
|
||||
val pollIcon = makeIcon(this, GoogleMaterial.Icon.gmd_poll, IconicsSize.dp(18))
|
||||
binding.addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null)
|
||||
|
||||
binding.actionPhotoTake.visible(Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(packageManager) != null)
|
||||
|
@ -754,15 +745,9 @@ class ComposeActivity :
|
|||
|
||||
private fun setStatusVisibility(visibility: Status.Visibility) {
|
||||
binding.composeOptionsBottomSheet.setStatusVisibility(visibility)
|
||||
binding.composeTootButton.setStatusVisibility(visibility)
|
||||
binding.composeTootButton.setStatusVisibility(binding.composeTootButton, visibility)
|
||||
|
||||
val iconRes = when (visibility) {
|
||||
Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp
|
||||
Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
|
||||
Status.Visibility.DIRECT -> R.drawable.ic_email_24dp
|
||||
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
|
||||
else -> R.drawable.ic_lock_open_24dp
|
||||
}
|
||||
val iconRes = visibility.iconRes() ?: R.drawable.ic_lock_open_24dp
|
||||
binding.composeToggleVisibilityButton.setImageResource(iconRes)
|
||||
if (viewModel.editing) {
|
||||
// Can't update visibility on published status
|
||||
|
|
|
@ -17,16 +17,13 @@
|
|||
package app.pachli.components.compose.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import app.pachli.R
|
||||
import app.pachli.core.designsystem.R as DR
|
||||
import app.pachli.core.network.model.Status
|
||||
import app.pachli.util.iconDrawable
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
|
||||
class TootButton
|
||||
@JvmOverloads constructor(
|
||||
|
@ -48,29 +45,16 @@ class TootButton
|
|||
setPadding(padding, 0, padding, 0)
|
||||
}
|
||||
|
||||
fun setStatusVisibility(visibility: Status.Visibility) {
|
||||
fun setStatusVisibility(view: View, visibility: Status.Visibility) {
|
||||
if (!smallStyle) {
|
||||
icon = when (visibility) {
|
||||
Status.Visibility.PUBLIC -> {
|
||||
setText(R.string.action_send_public)
|
||||
null
|
||||
}
|
||||
Status.Visibility.UNLISTED -> {
|
||||
setText(R.string.action_send)
|
||||
null
|
||||
}
|
||||
Status.Visibility.PRIVATE,
|
||||
Status.Visibility.DIRECT,
|
||||
-> {
|
||||
setText(R.string.action_send)
|
||||
IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply {
|
||||
sizeDp = 18
|
||||
colorInt = Color.WHITE
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
icon = visibility.iconDrawable(view)
|
||||
|
||||
when (visibility) {
|
||||
Status.Visibility.UNKNOWN -> { /* do nothing */ }
|
||||
Status.Visibility.PUBLIC -> setText(R.string.action_send_public)
|
||||
Status.Visibility.UNLISTED -> setText(R.string.action_send)
|
||||
Status.Visibility.PRIVATE -> setText(R.string.action_send)
|
||||
Status.Visibility.DIRECT -> setText(R.string.action_send)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import app.pachli.settings.switchPreference
|
|||
import app.pachli.util.getInitialLanguages
|
||||
import app.pachli.util.getLocaleList
|
||||
import app.pachli.util.getPachliDisplayName
|
||||
import app.pachli.util.iconRes
|
||||
import app.pachli.util.makeIcon
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
@ -207,9 +208,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
|
|||
setSummaryProvider { entry }
|
||||
val visibility = accountManager.activeAccount?.defaultPostPrivacy ?: Status.Visibility.PUBLIC
|
||||
value = visibility.serverString()
|
||||
setIcon(getIconForVisibility(visibility))
|
||||
visibility.iconRes()?.let { setIcon(it) }
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
setIcon(getIconForVisibility(Status.Visibility.byString(newValue as String)))
|
||||
Status.Visibility.byString(newValue as String).iconRes()?.let { setIcon(it) }
|
||||
syncWithServer(visibility = newValue)
|
||||
true
|
||||
}
|
||||
|
@ -344,17 +345,6 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
private fun getIconForVisibility(visibility: Status.Visibility): Int {
|
||||
return when (visibility) {
|
||||
Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
|
||||
|
||||
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
|
||||
|
||||
else -> R.drawable.ic_public_24dp
|
||||
}
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
private fun getIconForSensitivity(sensitive: Boolean): Int {
|
||||
return if (sensitive) {
|
||||
|
|
|
@ -21,8 +21,10 @@ import android.graphics.Color
|
|||
import androidx.annotation.Px
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.IconicsSize
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.size
|
||||
import com.mikepenz.iconics.utils.sizePx
|
||||
|
||||
fun makeIcon(context: Context, icon: GoogleMaterial.Icon, @Px iconSize: Int): IconicsDrawable {
|
||||
|
@ -31,3 +33,10 @@ fun makeIcon(context: Context, icon: GoogleMaterial.Icon, @Px iconSize: Int): Ic
|
|||
colorInt = MaterialColors.getColor(context, androidx.appcompat.R.attr.colorControlNormal, Color.BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
fun makeIcon(context: Context, icon: GoogleMaterial.Icon, iconSize: IconicsSize): IconicsDrawable {
|
||||
return IconicsDrawable(context, icon).apply {
|
||||
size = iconSize
|
||||
colorInt = MaterialColors.getColor(context, androidx.appcompat.R.attr.colorControlNormal, Color.BLACK)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ package app.pachli.util
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import app.pachli.R
|
||||
import app.pachli.core.network.model.Status
|
||||
|
@ -43,6 +45,24 @@ fun Status.Visibility?.description(context: Context): CharSequence {
|
|||
return context.getString(resource)
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
fun Status.Visibility?.iconRes(): Int? {
|
||||
this ?: return null
|
||||
|
||||
return when (this) {
|
||||
Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp
|
||||
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
|
||||
Status.Visibility.PRIVATE -> R.drawable.ic_lock_24dp
|
||||
Status.Visibility.DIRECT -> R.drawable.ic_email_24dp
|
||||
Status.Visibility.UNKNOWN -> return null
|
||||
}
|
||||
}
|
||||
|
||||
fun Status.Visibility?.iconDrawable(view: View): Drawable? {
|
||||
val resource = iconRes() ?: return null
|
||||
return AppCompatResources.getDrawable(view.context, resource)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An icon for this visibility scaled and coloured to match the text on [textView].
|
||||
* Returns null if visibility is [Status.Visibility.UNKNOWN].
|
||||
|
@ -50,19 +70,9 @@ fun Status.Visibility?.description(context: Context): CharSequence {
|
|||
fun Status.Visibility?.icon(textView: TextView): Drawable? {
|
||||
this ?: return null
|
||||
|
||||
val resource: Int = when (this) {
|
||||
Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp
|
||||
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
|
||||
Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
|
||||
Status.Visibility.DIRECT -> R.drawable.ic_email_24dp
|
||||
Status.Visibility.UNKNOWN -> return null
|
||||
}
|
||||
val visibilityDrawable = AppCompatResources.getDrawable(
|
||||
textView.context,
|
||||
resource,
|
||||
) ?: return null
|
||||
val drawable = iconDrawable(textView) ?: return null
|
||||
val size = textView.textSize.toInt()
|
||||
visibilityDrawable.setBounds(0, 0, size, size)
|
||||
visibilityDrawable.setTint(textView.currentTextColor)
|
||||
return visibilityDrawable
|
||||
drawable.setBounds(0, 0, size, size)
|
||||
drawable.setTint(textView.currentTextColor)
|
||||
return drawable
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#fff" android:pathData="M4,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4M12,11L20,6H4L12,11M4,18H20V8.37L12,13.36L4,8.37V18Z" />
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
|
||||
|
||||
</vector>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
|
||||
</vector>
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z" />
|
||||
</vector>
|
|
@ -198,7 +198,7 @@
|
|||
android:background="?attr/colorSurface"
|
||||
android:elevation="12dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="52dp"
|
||||
|
@ -210,28 +210,31 @@
|
|||
android:id="@+id/actionPhotoTake"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="48dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/action_photo_take"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
android:textColor="?android:textColorTertiary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionPhotoPick"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="48dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/action_add_media"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
android:textColor="?android:textColorTertiary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addPollTextActionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="48dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/action_add_poll"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
android:textColor="?android:textColorTertiary" />
|
||||
</LinearLayout>
|
||||
|
||||
<app.pachli.view.EmojiPicker
|
||||
|
@ -309,6 +312,7 @@
|
|||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_toggle_visibility"
|
||||
android:padding="4dp"
|
||||
app:tint="?attr/colorPrimary"
|
||||
app:tooltipText="@string/action_toggle_visibility"
|
||||
tools:src="@drawable/ic_public_24dp" />
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
android:id="@+id/publicRadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="48dp"
|
||||
android:button="@drawable/ic_public_24dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
|
@ -22,9 +21,7 @@
|
|||
android:id="@+id/unlistedRadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="48dp"
|
||||
android:button="@drawable/ic_lock_open_24dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
|
@ -36,10 +33,8 @@
|
|||
android:id="@+id/privateRadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:button="@drawable/ic_lock_outline_24dp"
|
||||
android:minHeight="48dp"
|
||||
android:button="@drawable/ic_lock_24dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/visibility_private"
|
||||
|
@ -50,8 +45,7 @@
|
|||
android:id="@+id/directRadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="48dp"
|
||||
android:button="@drawable/ic_email_24dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
|
@ -59,4 +53,4 @@
|
|||
android:textColor="?android:textColorTertiary"
|
||||
app:buttonTint="@color/compound_button_color" />
|
||||
|
||||
</merge>
|
||||
</merge>
|
||||
|
|
Loading…
Reference in New Issue