Add correct Content Description for the preview images at the Compose screen (#1188)

* Add correct Content Description for the preview images at the Compose screen. tuskyapp#1155

* Remove "unknown" string from resource. Format code

* Format code

* Update string resource for content description
This commit is contained in:
pandasoft0 2019-05-09 21:25:45 +03:00 committed by Konrad Pozniak
parent c75b046483
commit 5c61786e05
2 changed files with 105 additions and 56 deletions

View file

@ -133,6 +133,25 @@ import java.util.concurrent.CountDownLatch;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.core.view.inputmethod.InputConnectionCompat;
import androidx.core.view.inputmethod.InputContentInfoCompat;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;
import at.connyduck.sparkbutton.helpers.Utils; import at.connyduck.sparkbutton.helpers.Utils;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.SingleObserver; import io.reactivex.SingleObserver;
@ -318,12 +337,12 @@ public final class ComposeActivity
@Override @Override
public void onResponse(@NonNull Call<List<Emoji>> call, @NonNull Response<List<Emoji>> response) { public void onResponse(@NonNull Call<List<Emoji>> call, @NonNull Response<List<Emoji>> response) {
List<Emoji> emojiList = response.body(); List<Emoji> emojiList = response.body();
if(emojiList == null) { if (emojiList == null) {
emojiList = Collections.emptyList(); emojiList = Collections.emptyList();
} }
Collections.sort(emojiList, (a, b) -> Collections.sort(emojiList, (a, b) ->
a.getShortcode().toLowerCase(Locale.ROOT).compareTo( a.getShortcode().toLowerCase(Locale.ROOT).compareTo(
b.getShortcode().toLowerCase(Locale.ROOT))); b.getShortcode().toLowerCase(Locale.ROOT)));
setEmojiList(emojiList); setEmojiList(emojiList);
cacheInstanceMetadata(activeAccount); cacheInstanceMetadata(activeAccount);
} }
@ -357,7 +376,7 @@ public final class ComposeActivity
tootButton.setOnClickListener(v -> onSendClicked()); tootButton.setOnClickListener(v -> onSendClicked());
pickButton.setOnClickListener(v -> openPickDialog()); pickButton.setOnClickListener(v -> openPickDialog());
visibilityButton.setOnClickListener(v -> showComposeOptions()); visibilityButton.setOnClickListener(v -> showComposeOptions());
contentWarningButton.setOnClickListener(v-> onContentWarningChanged()); contentWarningButton.setOnClickListener(v -> onContentWarningChanged());
emojiButton.setOnClickListener(v -> showEmojis()); emojiButton.setOnClickListener(v -> showEmojis());
hideMediaToggle.setOnClickListener(v -> toggleHideMedia()); hideMediaToggle.setOnClickListener(v -> toggleHideMedia());
@ -481,7 +500,7 @@ public final class ComposeActivity
replyTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null); replyTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null);
replyTextView.setOnClickListener(v -> { replyTextView.setOnClickListener(v -> {
TransitionManager.beginDelayedTransition((ViewGroup)replyContentTextView.getParent()); TransitionManager.beginDelayedTransition((ViewGroup) replyContentTextView.getParent());
if (replyContentTextView.getVisibility() != View.VISIBLE) { if (replyContentTextView.getVisibility() != View.VISIBLE) {
replyContentTextView.setVisibility(View.VISIBLE); replyContentTextView.setVisibility(View.VISIBLE);
@ -547,7 +566,7 @@ public final class ComposeActivity
} }
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093 // work around Android platform bug -> https://issuetracker.google.com/issues/67102093
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.O || Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1 ) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O || Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
textEditor.setLayerType(View.LAYER_TYPE_SOFTWARE, null); textEditor.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} }
@ -586,7 +605,7 @@ public final class ComposeActivity
pickMedia(uri, mediaSize, description); pickMedia(uri, mediaSize, description);
} }
} else if (!ListUtils.isEmpty(mediaAttachments)) { } else if (!ListUtils.isEmpty(mediaAttachments)) {
for (int mediaIndex =0; mediaIndex < mediaAttachments.size(); ++mediaIndex) { for (int mediaIndex = 0; mediaIndex < mediaAttachments.size(); ++mediaIndex) {
Attachment media = mediaAttachments.get(mediaIndex); Attachment media = mediaAttachments.get(mediaIndex);
QueuedMedia.Type type; QueuedMedia.Type type;
switch (media.getType()) { switch (media.getType()) {
@ -650,15 +669,15 @@ public final class ComposeActivity
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
String text = intent.getStringExtra(Intent.EXTRA_TEXT); String text = intent.getStringExtra(Intent.EXTRA_TEXT);
String shareBody = null; String shareBody = null;
if(subject != null && text != null){ if (subject != null && text != null) {
if(!subject.equals(text) && !text.contains(subject)){ if (!subject.equals(text) && !text.contains(subject)) {
shareBody = String.format("%s\n%s", subject, text); shareBody = String.format("%s\n%s", subject, text);
}else{ } else {
shareBody = text; shareBody = text;
} }
}else if(text != null){ } else if (text != null) {
shareBody = text; shareBody = text;
}else if(subject != null){ } else if (subject != null) {
shareBody = subject; shareBody = subject;
} }
@ -725,10 +744,10 @@ public final class ComposeActivity
} }
private void updateHideMediaToggle() { private void updateHideMediaToggle() {
TransitionManager.beginDelayedTransition((ViewGroup)hideMediaToggle.getParent()); TransitionManager.beginDelayedTransition((ViewGroup) hideMediaToggle.getParent());
@ColorInt int color; @ColorInt int color;
if(mediaQueued.size() == 0) { if (mediaQueued.size() == 0) {
hideMediaToggle.setVisibility(View.GONE); hideMediaToggle.setVisibility(View.GONE);
} else { } else {
hideMediaToggle.setVisibility(View.VISIBLE); hideMediaToggle.setVisibility(View.VISIBLE);
@ -854,7 +873,7 @@ public final class ComposeActivity
@Override @Override
public void onStateChanged(@NonNull View bottomSheet, int newState) { public void onStateChanged(@NonNull View bottomSheet, int newState) {
//Wait until bottom sheet is not collapsed and show next screen after //Wait until bottom sheet is not collapsed and show next screen after
if (newState==BottomSheetBehavior.STATE_COLLAPSED){ if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
addMediaBehavior.setBottomSheetCallback(null); addMediaBehavior.setBottomSheetCallback(null);
if (ContextCompat.checkSelfPermission(ComposeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) if (ContextCompat.checkSelfPermission(ComposeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
@ -1112,7 +1131,7 @@ public final class ComposeActivity
// Continue only if the File was successfully created // Continue only if the File was successfully created
if (photoFile != null) { if (photoFile != null) {
photoUploadUri = FileProvider.getUriForFile(this, photoUploadUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID+".fileprovider", BuildConfig.APPLICATION_ID + ".fileprovider",
photoFile); photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUploadUri); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUploadUri);
startActivityForResult(intent, MEDIA_TAKE_PHOTO_RESULT); startActivityForResult(intent, MEDIA_TAKE_PHOTO_RESULT);
@ -1169,9 +1188,9 @@ public final class ComposeActivity
.into(view); .into(view);
} }
view.setOnClickListener(v -> onMediaClick(item, v)); view.setOnClickListener(v -> onMediaClick(item, v));
view.setContentDescription(getString(R.string.action_delete));
mediaPreviewBar.addView(view); mediaPreviewBar.addView(view);
mediaQueued.add(item); mediaQueued.add(item);
updateContentDescription(item);
int queuedCount = mediaQueued.size(); int queuedCount = mediaQueued.size();
if (queuedCount == 1) { if (queuedCount == 1) {
// If there's one video in the queue it is full, so disable the button to queue more. // If there's one video in the queue it is full, so disable the button to queue more.
@ -1201,6 +1220,33 @@ public final class ComposeActivity
} }
} }
private void updateContentDescriptionForAllImages() {
List<QueuedMedia> items = new ArrayList<>(mediaQueued);
for (QueuedMedia media : items) {
updateContentDescription(media);
}
}
private void updateContentDescription(QueuedMedia item) {
if (item.preview != null) {
String imageId;
if (!TextUtils.isEmpty(item.description)) {
imageId = item.description;
} else {
int idx = getImageIdx(item);
if (idx < 0)
imageId = null;
else
imageId = Integer.toString(idx + 1);
}
item.preview.setContentDescription(getString(R.string.compose_preview_image_description, imageId));
}
}
private int getImageIdx(QueuedMedia item) {
return mediaQueued.indexOf(item);
}
private void onMediaClick(QueuedMedia item, View view) { private void onMediaClick(QueuedMedia item, View view) {
PopupMenu popup = new PopupMenu(this, view); PopupMenu popup = new PopupMenu(this, view);
final int addCaptionId = 1; final int addCaptionId = 1;
@ -1239,7 +1285,8 @@ public final class ComposeActivity
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(new SingleObserver<Bitmap>() { .subscribe(new SingleObserver<Bitmap>() {
@Override @Override
public void onSubscribe(Disposable d) {} public void onSubscribe(Disposable d) {
}
@Override @Override
public void onSuccess(Bitmap bitmap) { public void onSuccess(Bitmap bitmap) {
@ -1247,7 +1294,8 @@ public final class ComposeActivity
} }
@Override @Override
public void onError(Throwable e) { } public void onError(Throwable e) {
}
}); });
@ -1264,38 +1312,39 @@ public final class ComposeActivity
input.setLines(2); input.setLines(2);
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
input.setText(item.description); input.setText(item.description);
input.setFilters(new InputFilter[] { new InputFilter.LengthFilter(MEDIA_DESCRIPTION_CHARACTER_LIMIT) }); input.setFilters(new InputFilter[]{new InputFilter.LengthFilter(MEDIA_DESCRIPTION_CHARACTER_LIMIT)});
DialogInterface.OnClickListener okListener = (dialog, which) -> { DialogInterface.OnClickListener okListener = (dialog, which) -> {
Runnable updateDescription = () -> { Runnable updateDescription = () -> {
mastodonApi.updateMedia(item.id, input.getText().toString()).enqueue(new Callback<Attachment>() { mastodonApi.updateMedia(item.id, input.getText().toString()).enqueue(new Callback<Attachment>() {
@Override @Override
public void onResponse(@NonNull Call<Attachment> call, @NonNull Response<Attachment> response) { public void onResponse(@NonNull Call<Attachment> call, @NonNull Response<Attachment> response) {
Attachment attachment = response.body(); Attachment attachment = response.body();
if (response.isSuccessful() && attachment != null) { if (response.isSuccessful() && attachment != null) {
item.description = attachment.getDescription(); item.description = attachment.getDescription();
item.preview.setChecked(item.description != null && !item.description.isEmpty()); item.preview.setChecked(item.description != null && !item.description.isEmpty());
dialog.dismiss(); dialog.dismiss();
} else { updateContentDescription(item);
showFailedCaptionMessage(); } else {
}
item.updateDescription = null;
}
@Override
public void onFailure(@NonNull Call<Attachment> call, @NonNull Throwable t) {
showFailedCaptionMessage(); showFailedCaptionMessage();
item.updateDescription = null;
} }
}); item.updateDescription = null;
}; }
if (item.readyStage == QueuedMedia.ReadyStage.UPLOADED) { @Override
updateDescription.run(); public void onFailure(@NonNull Call<Attachment> call, @NonNull Throwable t) {
} else { showFailedCaptionMessage();
// media is still uploading, queue description update for when it finishes item.updateDescription = null;
item.updateDescription = updateDescription; }
} });
};
if (item.readyStage == QueuedMedia.ReadyStage.UPLOADED) {
updateDescription.run();
} else {
// media is still uploading, queue description update for when it finishes
item.updateDescription = updateDescription;
}
}; };
AlertDialog dialog = new AlertDialog.Builder(this) AlertDialog dialog = new AlertDialog.Builder(this)
@ -1323,7 +1372,7 @@ public final class ComposeActivity
if (mediaQueued.size() == 0) { if (mediaQueued.size() == 0) {
updateHideMediaToggle(); updateHideMediaToggle();
} }
updateContentDescriptionForAllImages();
enableButton(pickButton, true, true); enableButton(pickButton, true, true);
cancelReadyingMedia(item); cancelReadyingMedia(item);
} }
@ -1345,7 +1394,7 @@ public final class ComposeActivity
public void onSuccess(File tempFile) { public void onSuccess(File tempFile) {
item.uri = FileProvider.getUriForFile( item.uri = FileProvider.getUriForFile(
ComposeActivity.this, ComposeActivity.this,
BuildConfig.APPLICATION_ID+".fileprovider", BuildConfig.APPLICATION_ID + ".fileprovider",
tempFile); tempFile);
uploadMedia(item); uploadMedia(item);
} }
@ -1524,7 +1573,7 @@ public final class ComposeActivity
private void showContentWarning(boolean show) { private void showContentWarning(boolean show) {
statusHideText = show; statusHideText = show;
TransitionManager.beginDelayedTransition((ViewGroup)contentWarningBar.getParent()); TransitionManager.beginDelayedTransition((ViewGroup) contentWarningBar.getParent());
int color; int color;
if (show) { if (show) {
statusMarkSensitive = true; statusMarkSensitive = true;
@ -1556,9 +1605,9 @@ public final class ComposeActivity
@Override @Override
public void onBackPressed() { public void onBackPressed() {
// Acting like a teen: deliberately ignoring parent. // Acting like a teen: deliberately ignoring parent.
if(composeOptionsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED || if (composeOptionsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED ||
addMediaBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED || addMediaBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED ||
emojiBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED ) { emojiBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
composeOptionsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); composeOptionsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
addMediaBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); addMediaBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
emojiBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); emojiBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
@ -1690,14 +1739,14 @@ public final class ComposeActivity
@Override @Override
public void onEmojiSelected(@NotNull String shortcode) { public void onEmojiSelected(@NotNull String shortcode) {
textEditor.getText().insert(textEditor.getSelectionStart(), ":"+shortcode+": "); textEditor.getText().insert(textEditor.getSelectionStart(), ":" + shortcode + ": ");
} }
private void loadCachedInstanceMetadata(@NotNull AccountEntity activeAccount) { private void loadCachedInstanceMetadata(@NotNull AccountEntity activeAccount) {
InstanceEntity instanceEntity = database.instanceDao() InstanceEntity instanceEntity = database.instanceDao()
.loadMetadataForInstance(activeAccount.getDomain()); .loadMetadataForInstance(activeAccount.getDomain());
if(instanceEntity != null) { if (instanceEntity != null) {
Integer max = instanceEntity.getMaximumTootCharacters(); Integer max = instanceEntity.getMaximumTootCharacters();
maximumTootCharacters = (max == null ? STATUS_CHARACTER_LIMIT : max); maximumTootCharacters = (max == null ? STATUS_CHARACTER_LIMIT : max);
setEmojiList(instanceEntity.getEmojiList()); setEmojiList(instanceEntity.getEmojiList());
@ -1722,14 +1771,13 @@ public final class ComposeActivity
} }
// Accessors for testing, hence package scope // Accessors for testing, hence package scope
int getMaximumTootCharacters() int getMaximumTootCharacters() {
{
return maximumTootCharacters; return maximumTootCharacters;
} }
static boolean canHandleMimeType(@Nullable String mimeType) { static boolean canHandleMimeType(@Nullable String mimeType) {
return (mimeType != null && return (mimeType != null &&
(mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType.equals("text/plain"))); (mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType.equals("text/plain")));
} }
public static final class QueuedMedia { public static final class QueuedMedia {

View file

@ -466,6 +466,7 @@
<string name="pref_title_bot_overlay">Show indicator for bots</string> <string name="pref_title_bot_overlay">Show indicator for bots</string>
<string name="notification_clear_text">Are you sure you want to permanently clear all your notifications?</string> <string name="notification_clear_text">Are you sure you want to permanently clear all your notifications?</string>
<string name="compose_preview_image_description">Actions for image %s</string>
<string name="poll_info_format"> <string name="poll_info_format">
<!-- 15 votes • 1 hour left --> <!-- 15 votes • 1 hour left -->