From 5c61786e0553a8636694a82e320c5cc673c61141 Mon Sep 17 00:00:00 2001 From: pandasoft0 Date: Thu, 9 May 2019 21:25:45 +0300 Subject: [PATCH] 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 --- .../keylesspalace/tusky/ComposeActivity.java | 160 ++++++++++++------ app/src/main/res/values/strings.xml | 1 + 2 files changed, 105 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 840be168e..0b2371de8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -133,6 +133,25 @@ import java.util.concurrent.CountDownLatch; 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 io.reactivex.Single; import io.reactivex.SingleObserver; @@ -318,12 +337,12 @@ public final class ComposeActivity @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { List emojiList = response.body(); - if(emojiList == null) { + if (emojiList == null) { emojiList = Collections.emptyList(); } Collections.sort(emojiList, (a, b) -> - a.getShortcode().toLowerCase(Locale.ROOT).compareTo( - b.getShortcode().toLowerCase(Locale.ROOT))); + a.getShortcode().toLowerCase(Locale.ROOT).compareTo( + b.getShortcode().toLowerCase(Locale.ROOT))); setEmojiList(emojiList); cacheInstanceMetadata(activeAccount); } @@ -357,7 +376,7 @@ public final class ComposeActivity tootButton.setOnClickListener(v -> onSendClicked()); pickButton.setOnClickListener(v -> openPickDialog()); visibilityButton.setOnClickListener(v -> showComposeOptions()); - contentWarningButton.setOnClickListener(v-> onContentWarningChanged()); + contentWarningButton.setOnClickListener(v -> onContentWarningChanged()); emojiButton.setOnClickListener(v -> showEmojis()); hideMediaToggle.setOnClickListener(v -> toggleHideMedia()); @@ -481,7 +500,7 @@ public final class ComposeActivity replyTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null); replyTextView.setOnClickListener(v -> { - TransitionManager.beginDelayedTransition((ViewGroup)replyContentTextView.getParent()); + TransitionManager.beginDelayedTransition((ViewGroup) replyContentTextView.getParent()); if (replyContentTextView.getVisibility() != 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 - 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); } @@ -586,7 +605,7 @@ public final class ComposeActivity pickMedia(uri, mediaSize, description); } } 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); QueuedMedia.Type type; switch (media.getType()) { @@ -650,15 +669,15 @@ public final class ComposeActivity String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); String text = intent.getStringExtra(Intent.EXTRA_TEXT); String shareBody = null; - if(subject != null && text != null){ - if(!subject.equals(text) && !text.contains(subject)){ + if (subject != null && text != null) { + if (!subject.equals(text) && !text.contains(subject)) { shareBody = String.format("%s\n%s", subject, text); - }else{ + } else { shareBody = text; } - }else if(text != null){ + } else if (text != null) { shareBody = text; - }else if(subject != null){ + } else if (subject != null) { shareBody = subject; } @@ -725,10 +744,10 @@ public final class ComposeActivity } private void updateHideMediaToggle() { - TransitionManager.beginDelayedTransition((ViewGroup)hideMediaToggle.getParent()); + TransitionManager.beginDelayedTransition((ViewGroup) hideMediaToggle.getParent()); @ColorInt int color; - if(mediaQueued.size() == 0) { + if (mediaQueued.size() == 0) { hideMediaToggle.setVisibility(View.GONE); } else { hideMediaToggle.setVisibility(View.VISIBLE); @@ -854,7 +873,7 @@ public final class ComposeActivity @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { //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); if (ContextCompat.checkSelfPermission(ComposeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { @@ -1112,7 +1131,7 @@ public final class ComposeActivity // Continue only if the File was successfully created if (photoFile != null) { photoUploadUri = FileProvider.getUriForFile(this, - BuildConfig.APPLICATION_ID+".fileprovider", + BuildConfig.APPLICATION_ID + ".fileprovider", photoFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUploadUri); startActivityForResult(intent, MEDIA_TAKE_PHOTO_RESULT); @@ -1169,9 +1188,9 @@ public final class ComposeActivity .into(view); } view.setOnClickListener(v -> onMediaClick(item, v)); - view.setContentDescription(getString(R.string.action_delete)); mediaPreviewBar.addView(view); mediaQueued.add(item); + updateContentDescription(item); int queuedCount = mediaQueued.size(); if (queuedCount == 1) { // 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 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) { PopupMenu popup = new PopupMenu(this, view); final int addCaptionId = 1; @@ -1239,7 +1285,8 @@ public final class ComposeActivity .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .subscribe(new SingleObserver() { @Override - public void onSubscribe(Disposable d) {} + public void onSubscribe(Disposable d) { + } @Override public void onSuccess(Bitmap bitmap) { @@ -1247,7 +1294,8 @@ public final class ComposeActivity } @Override - public void onError(Throwable e) { } + public void onError(Throwable e) { + } }); @@ -1264,38 +1312,39 @@ public final class ComposeActivity input.setLines(2); input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 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) -> { - Runnable updateDescription = () -> { - mastodonApi.updateMedia(item.id, input.getText().toString()).enqueue(new Callback() { - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - Attachment attachment = response.body(); - if (response.isSuccessful() && attachment != null) { - item.description = attachment.getDescription(); - item.preview.setChecked(item.description != null && !item.description.isEmpty()); - dialog.dismiss(); - } else { - showFailedCaptionMessage(); - } - item.updateDescription = null; - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Runnable updateDescription = () -> { + mastodonApi.updateMedia(item.id, input.getText().toString()).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + Attachment attachment = response.body(); + if (response.isSuccessful() && attachment != null) { + item.description = attachment.getDescription(); + item.preview.setChecked(item.description != null && !item.description.isEmpty()); + dialog.dismiss(); + updateContentDescription(item); + } else { showFailedCaptionMessage(); - item.updateDescription = null; } - }); - }; + item.updateDescription = null; + } - if (item.readyStage == QueuedMedia.ReadyStage.UPLOADED) { - updateDescription.run(); - } else { - // media is still uploading, queue description update for when it finishes - item.updateDescription = updateDescription; - } + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + showFailedCaptionMessage(); + item.updateDescription = null; + } + }); + }; + + 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) @@ -1323,7 +1372,7 @@ public final class ComposeActivity if (mediaQueued.size() == 0) { updateHideMediaToggle(); } - + updateContentDescriptionForAllImages(); enableButton(pickButton, true, true); cancelReadyingMedia(item); } @@ -1345,7 +1394,7 @@ public final class ComposeActivity public void onSuccess(File tempFile) { item.uri = FileProvider.getUriForFile( ComposeActivity.this, - BuildConfig.APPLICATION_ID+".fileprovider", + BuildConfig.APPLICATION_ID + ".fileprovider", tempFile); uploadMedia(item); } @@ -1524,7 +1573,7 @@ public final class ComposeActivity private void showContentWarning(boolean show) { statusHideText = show; - TransitionManager.beginDelayedTransition((ViewGroup)contentWarningBar.getParent()); + TransitionManager.beginDelayedTransition((ViewGroup) contentWarningBar.getParent()); int color; if (show) { statusMarkSensitive = true; @@ -1556,9 +1605,9 @@ public final class ComposeActivity @Override public void onBackPressed() { // Acting like a teen: deliberately ignoring parent. - if(composeOptionsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED || + if (composeOptionsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED || addMediaBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED || - emojiBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED ) { + emojiBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { composeOptionsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); addMediaBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); emojiBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -1690,14 +1739,14 @@ public final class ComposeActivity @Override public void onEmojiSelected(@NotNull String shortcode) { - textEditor.getText().insert(textEditor.getSelectionStart(), ":"+shortcode+": "); + textEditor.getText().insert(textEditor.getSelectionStart(), ":" + shortcode + ": "); } private void loadCachedInstanceMetadata(@NotNull AccountEntity activeAccount) { InstanceEntity instanceEntity = database.instanceDao() .loadMetadataForInstance(activeAccount.getDomain()); - if(instanceEntity != null) { + if (instanceEntity != null) { Integer max = instanceEntity.getMaximumTootCharacters(); maximumTootCharacters = (max == null ? STATUS_CHARACTER_LIMIT : max); setEmojiList(instanceEntity.getEmojiList()); @@ -1722,14 +1771,13 @@ public final class ComposeActivity } // Accessors for testing, hence package scope - int getMaximumTootCharacters() - { + int getMaximumTootCharacters() { return maximumTootCharacters; } static boolean canHandleMimeType(@Nullable String mimeType) { 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 { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51e4cef68..a8730d6ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -466,6 +466,7 @@ Show indicator for bots Are you sure you want to permanently clear all your notifications? + Actions for image %s