diff --git a/app/build.gradle b/app/build.gradle index 71c0eef345..46b97f466a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -161,8 +161,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.4' } -def canonicalVersionCode = 333 -def canonicalVersionName = "1.16.5" +def canonicalVersionCode = 334 +def canonicalVersionName = "1.16.6" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt index 358a9d326b..d0b101a9f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt @@ -5,8 +5,9 @@ import androidx.annotation.AttrRes /** * Represents an action to be rendered */ -data class ActionItem( +data class ActionItem @JvmOverloads constructor( @AttrRes val iconRes: Int, val title: CharSequence, - val action: Runnable + val action: Runnable, + val contentDescription: String? = null ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt index 65fb1ddbbc..c86b40dfa5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt @@ -77,6 +77,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) { context.theme.resolveAttribute(model.item.iconRes, typedValue, true) icon.setImageDrawable(ContextCompat.getDrawable(context, typedValue.resourceId)) } + itemView.contentDescription = model.item.contentDescription title.text = model.item.title itemView.setOnClickListener { model.item.action.run() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 37db70effe..3ecb1c11ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -858,7 +858,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val recipient = viewModel.recipient ?: return if (!isShowingMentionCandidatesView) { additionalContentContainer.removeAllViews() - val view = MentionCandidatesView(this) + val view = MentionCandidatesView(this).apply { + contentDescription = context.getString(R.string.AccessibilityId_mentions_list) + } view.glide = glide view.onCandidateSelected = { handleMentionSelected(it) } additionalContentContainer.addView(view) @@ -1035,7 +1037,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe override fun block(deleteThread: Boolean) { val title = R.string.RecipientPreferenceActivity_block_this_contact_question val message = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact - AlertDialog.Builder(this) + val dialog = AlertDialog.Builder(this) .setTitle(title) .setMessage(message) .setNegativeButton(android.R.string.cancel, null) @@ -1046,6 +1048,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe finish() } }.show() + val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE) + button.setContentDescription("Confirm block") } override fun copySessionID(sessionId: String) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java index 995dcda2f2..b3c151690f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java @@ -660,10 +660,14 @@ public final class ConversationReactionOverlay extends FrameLayout { String userPublicKey = TextSecurePreferences.getLocalNumber(getContext()); // Select message - items.add(new ActionItem(R.attr.menu_select_icon, getContext().getResources().getString(R.string.conversation_context__menu_select), () -> handleActionItemClicked(Action.SELECT))); + items.add(new ActionItem(R.attr.menu_select_icon, getContext().getResources().getString(R.string.conversation_context__menu_select), () -> handleActionItemClicked(Action.SELECT), + getContext().getResources().getString(R.string.AccessibilityId_select))); // Reply if (!message.isPending() && !message.isFailed()) { - items.add(new ActionItem(R.attr.menu_reply_icon, getContext().getResources().getString(R.string.conversation_context__menu_reply), () -> handleActionItemClicked(Action.REPLY))); + items.add( + new ActionItem(R.attr.menu_reply_icon, getContext().getResources().getString(R.string.conversation_context__menu_reply), () -> handleActionItemClicked(Action.REPLY), + getContext().getResources().getString(R.string.AccessibilityId_reply_message)) + ); } // Copy message text if (!containsControlMessage && hasText) { @@ -671,11 +675,17 @@ public final class ConversationReactionOverlay extends FrameLayout { } // Copy Session ID if (recipient.isGroupRecipient() && !recipient.isOpenGroupRecipient() && !message.getRecipient().getAddress().toString().equals(userPublicKey)) { - items.add(new ActionItem(R.attr.menu_copy_icon, getContext().getResources().getString(R.string.activity_conversation_menu_copy_session_id), () -> handleActionItemClicked(Action.COPY_SESSION_ID))); + items.add(new ActionItem( + R.attr.menu_copy_icon, getContext().getResources().getString(R.string.activity_conversation_menu_copy_session_id), () -> handleActionItemClicked(Action.COPY_SESSION_ID)) + ); } // Delete message if (ConversationMenuItemHelper.userCanDeleteSelectedItems(getContext(), message, openGroup, userPublicKey, blindedPublicKey)) { - items.add(new ActionItem(R.attr.menu_trash_icon, getContext().getResources().getString(R.string.delete), () -> handleActionItemClicked(Action.DELETE))); + items.add(new ActionItem(R.attr.menu_trash_icon, getContext().getResources().getString(R.string.delete), + () -> handleActionItemClicked(Action.DELETE), + getContext().getResources().getString(R.string.AccessibilityId_delete_message) + ) + ); } // Ban user if (ConversationMenuItemHelper.userCanBanSelectedUsers(getContext(), message, openGroup, userPublicKey, blindedPublicKey)) { @@ -695,7 +705,9 @@ public final class ConversationReactionOverlay extends FrameLayout { } // Save media if (message.isMms() && ((MediaMmsMessageRecord)message).containsMediaSlide()) { - items.add(new ActionItem(R.attr.menu_save_icon, getContext().getResources().getString(R.string.conversation_context_image__save_attachment), () -> handleActionItemClicked(Action.DOWNLOAD))); + items.add(new ActionItem(R.attr.menu_save_icon, getContext().getResources().getString(R.string.conversation_context_image__save_attachment), () -> handleActionItemClicked(Action.DOWNLOAD), + getContext().getResources().getString(R.string.AccessibilityId_save_attachment)) + ); } backgroundView.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt index 7ac70b843a..73e2d571c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt @@ -57,9 +57,9 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li val attachmentButtonsContainerHeight: Int get() = binding.attachmentsButtonContainer.height - private val attachmentsButton by lazy { InputBarButton(context, R.drawable.ic_plus_24) } - private val microphoneButton by lazy { InputBarButton(context, R.drawable.ic_microphone) } - private val sendButton by lazy { InputBarButton(context, R.drawable.ic_arrow_up, true) } + private val attachmentsButton by lazy { InputBarButton(context, R.drawable.ic_plus_24).apply { contentDescription = context.getString(R.string.AccessibilityId_attachments_button)} } + private val microphoneButton by lazy { InputBarButton(context, R.drawable.ic_microphone).apply { contentDescription = context.getString(R.string.AccessibilityId_microphone_button)} } + private val sendButton by lazy { InputBarButton(context, R.drawable.ic_arrow_up, true).apply { contentDescription = context.getString(R.string.AccessibilityId_send_message_button)} } // region Lifecycle constructor(context: Context) : super(context) { initialize() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/mentions/MentionCandidatesView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/mentions/MentionCandidatesView.kt index 401ccaa3c2..e62f7f8f85 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/mentions/MentionCandidatesView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/mentions/MentionCandidatesView.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.ListView import dagger.hilt.android.AndroidEntryPoint +import network.loki.messenger.R import org.session.libsession.messaging.mentions.Mention import org.thoughtcrime.securesms.database.LokiThreadDatabase import org.thoughtcrime.securesms.mms.GlideRequests @@ -41,7 +42,9 @@ class MentionCandidatesView(context: Context, attrs: AttributeSet?, defStyleAttr override fun getItem(position: Int): Mention { return candidates[position] } override fun getView(position: Int, cellToBeReused: View?, parent: ViewGroup): View { - val cell = cellToBeReused as MentionCandidateView? ?: MentionCandidateView(context) + val cell = cellToBeReused as MentionCandidateView? ?: MentionCandidateView(context).apply { + contentDescription = context.getString(R.string.AccessibilityId_contact) + } val mentionCandidate = getItem(position) cell.glide = glide cell.candidate = mentionCandidate diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index c35dabc65e..90f415c9c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2.menus import android.annotation.SuppressLint import android.content.Context +import android.content.DialogInterface import android.content.Intent import android.graphics.BitmapFactory import android.graphics.PorterDuff @@ -186,7 +187,7 @@ object ConversationMenuHelper { private fun call(context: Context, thread: Recipient) { if (!TextSecurePreferences.isCallNotificationsEnabled(context)) { - AlertDialog.Builder(context) + val dialog = AlertDialog.Builder(context) .setTitle(R.string.ConversationActivity_call_title) .setMessage(R.string.ConversationActivity_call_prompt) .setPositiveButton(R.string.activity_settings_title) { _, _ -> @@ -195,7 +196,10 @@ object ConversationMenuHelper { } .setNeutralButton(R.string.cancel) { d, _ -> d.dismiss() - }.show() + }.create() + dialog.getButton(DialogInterface.BUTTON_POSITIVE)?.contentDescription = context.getString(R.string.AccessibilityId_settings) + dialog.getButton(DialogInterface.BUTTON_NEGATIVE)?.contentDescription = context.getString(R.string.AccessibilityId_cancel_button) + dialog.show() return } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt index a4e4a52d5b..3e370104ea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt @@ -31,6 +31,7 @@ class ControlMessageView : LinearLayout { binding.dateBreakTextView.showDateBreak(message, previous) binding.iconImageView.visibility = View.GONE var messageBody: CharSequence = message.getDisplayBody(context) + binding.root.contentDescription= null when { message.isExpirationTimerUpdate -> { binding.iconImageView.setImageDrawable( @@ -46,6 +47,7 @@ class ControlMessageView : LinearLayout { } message.isMessageRequestResponse -> { messageBody = context.getString(R.string.message_requests_accepted) + binding.root.contentDescription=context.getString(R.string.AccessibilityId_message_request_config_message) } message.isCallLog -> { val drawable = when { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 1890e604a3..2c04bcfce3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -13,6 +13,9 @@ import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.View import android.widget.LinearLayout +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat @@ -194,7 +197,7 @@ class VisibleMessageView : LinearLayout { binding.dateBreakTextView.isVisible = showDateBreak // Message status indicator if (message.isOutgoing) { - val (iconID, iconColor, textId) = getMessageStatusImage(message) + val (iconID, iconColor, textId, contentDescription) = getMessageStatusImage(message) if (textId != null) { binding.messageStatusTextView.setText(textId) @@ -209,6 +212,7 @@ class VisibleMessageView : LinearLayout { } binding.messageStatusImageView.setImageDrawable(drawable) } + binding.messageStatusImageView.contentDescription = contentDescription val lastMessageID = mmsSmsDb.getLastMessageID(message.threadId) binding.messageStatusTextView.isVisible = ( @@ -283,17 +287,43 @@ class VisibleMessageView : LinearLayout { } } - private fun getMessageStatusImage(message: MessageRecord): Triple { + data class MessageStatusInfo(@DrawableRes val iconId: Int?, + @ColorInt val iconTint: Int?, + @StringRes val messageText: Int?, + val contentDescription: String?) + + private fun getMessageStatusImage(message: MessageRecord): MessageStatusInfo { return when { - !message.isOutgoing -> Triple(null, null, null) + !message.isOutgoing -> MessageStatusInfo(null, + null, + null, + null) message.isFailed -> - Triple(R.drawable.ic_delivery_status_failed, resources.getColor(R.color.destructive, context.theme), R.string.delivery_status_failed) + MessageStatusInfo( + R.drawable.ic_delivery_status_failed, + resources.getColor(R.color.destructive, context.theme), + R.string.delivery_status_failed, + null + ) message.isPending -> - Triple(R.drawable.ic_delivery_status_sending, context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sending) + MessageStatusInfo( + R.drawable.ic_delivery_status_sending, + context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sending, + context.getString(R.string.AccessibilityId_message_sent_status_pending) + ) message.isRead -> - Triple(R.drawable.ic_delivery_status_read, context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_read) + MessageStatusInfo( + R.drawable.ic_delivery_status_read, + context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_read, + null + ) else -> - Triple(R.drawable.ic_delivery_status_sent, context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sent) + MessageStatusInfo( + R.drawable.ic_delivery_status_sent, + context.getColorFromAttr(R.attr.message_status_color), + R.string.delivery_status_sent, + context.getString(R.string.AccessibilityId_message_sent_status_tick) + ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 999dad001d..2d7e6dae59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.permissions; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -12,6 +13,7 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.ViewGroup; import android.view.WindowManager; +import android.widget.Button; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -162,12 +164,13 @@ public class Permissions { @SuppressWarnings("ConstantConditions") private void executePermissionsRequestWithRationale(PermissionsRequest request) { - RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) - .setPositiveButton(R.string.Permissions_continue, (dialog, which) -> executePermissionsRequest(request)) - .setNegativeButton(R.string.Permissions_not_now, (dialog, which) -> executeNoPermissionsRequest(request)) - .show() - .getWindow() - .setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); + AlertDialog dialog = RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) + .setPositiveButton(R.string.Permissions_continue, (d, which) -> executePermissionsRequest(request)) + .setNegativeButton(R.string.Permissions_not_now, (d, which) -> executeNoPermissionsRequest(request)) + .show(); + dialog.getWindow().setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); + Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + positiveButton.setContentDescription("Continue"); } private void executePermissionsRequest(PermissionsRequest request) { @@ -353,12 +356,17 @@ public class Permissions { Context context = this.context.get(); if (context != null) { - new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog) + AlertDialog alertDialog = new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog) .setTitle(R.string.Permissions_permission_required) .setMessage(message) .setPositiveButton(R.string.Permissions_continue, (dialog, which) -> context.startActivity(getApplicationSettingsIntent(context))) .setNegativeButton(android.R.string.cancel, null) - .show(); + .create(); + Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (positiveButton != null) { + positiveButton.setContentDescription(context.getString(R.string.AccessibilityId_continue)); + } + alertDialog.show(); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java index b5774447ee..ac03efa362 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java @@ -5,10 +5,12 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.KeyguardManager; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; +import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -179,7 +181,7 @@ public class PrivacySettingsPreferenceFragment extends ListSummaryPreferenceFrag boolean val = (boolean) newValue; if (val) { // check if we've shown the info dialog and check for microphone permissions - new AlertDialog.Builder(new ContextThemeWrapper(context.requireContext(), R.style.ThemeOverlay_Session_AlertDialog)) + AlertDialog dialog = new AlertDialog.Builder(new ContextThemeWrapper(context.requireContext(), R.style.ThemeOverlay_Session_AlertDialog)) .setTitle(R.string.dialog_voice_video_title) .setMessage(R.string.dialog_voice_video_message) .setPositiveButton(R.string.dialog_link_preview_enable_button_title, (d, w) -> { @@ -189,7 +191,9 @@ public class PrivacySettingsPreferenceFragment extends ListSummaryPreferenceFrag }) .show(); - return false; + Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + positiveButton.setContentDescription("Enable"); + return false; } else { return true; } diff --git a/app/src/main/res/layout-sw400dp/activity_display_name.xml b/app/src/main/res/layout-sw400dp/activity_display_name.xml index 50986e7dd4..4d4ff30406 100644 --- a/app/src/main/res/layout-sw400dp/activity_display_name.xml +++ b/app/src/main/res/layout-sw400dp/activity_display_name.xml @@ -33,6 +33,7 @@ + android:text="@string/activity_pn_mode_slow_mode" + /> @@ -176,6 +180,7 @@ + android:text="@string/activity_conversation_block_user"/> @@ -72,6 +75,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:contentDescription="@string/AccessibilityId_group_name" android:textColor="?android:textColorPrimary" android:textSize="@dimen/very_large_font_size" android:textStyle="bold" @@ -113,10 +117,10 @@ style="@style/Widget.Session.Button.Common.ProminentOutline" android:layout_width="wrap_content" android:layout_height="@dimen/small_button_height" - android:layout_marginTop="@dimen/small_spacing" + android:layout_marginVertical="@dimen/small_spacing" android:layout_marginEnd="@dimen/medium_spacing" android:layout_marginStart="@dimen/small_spacing" - android:layout_marginBottom="@dimen/small_spacing" + android:contentDescription="@string/AccessibilityId_add_members" android:paddingStart="@dimen/medium_spacing" android:paddingEnd="@dimen/medium_spacing" android:text="@string/activity_edit_closed_group_add_members" /> diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 8ecb983f4b..6eea402fbc 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -34,7 +34,8 @@ android:layout_height="@dimen/small_profile_picture_size" android:layout_alignParentLeft="true" android:layout_centerVertical="true" - android:layout_marginLeft="9dp" /> + android:layout_marginLeft="9dp" + android:contentDescription="@string/AccessibilityId_user_settings" />