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:
parent
c75b046483
commit
5c61786e05
2 changed files with 105 additions and 56 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
Loading…
Reference in a new issue