Set caption for media (#500)
This commit is contained in:
parent
6152043df3
commit
0426e8725a
3 changed files with 125 additions and 17 deletions
|
@ -62,12 +62,15 @@ import android.text.style.URLSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -93,6 +96,8 @@ import com.keylesspalace.tusky.util.StringUtils;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
import com.keylesspalace.tusky.view.EditTextTyped;
|
import com.keylesspalace.tusky.view.EditTextTyped;
|
||||||
import com.keylesspalace.tusky.view.ProgressImageView;
|
import com.keylesspalace.tusky.view.ProgressImageView;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
import com.varunest.sparkbutton.helpers.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -249,17 +254,17 @@ public final class ComposeActivity extends BaseActivity
|
||||||
inReplyToId = null;
|
inReplyToId = null;
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
|
|
||||||
if(startingVisibility == Status.Visibility.UNKNOWN) {
|
if (startingVisibility == Status.Visibility.UNKNOWN) {
|
||||||
Status.Visibility replyVisibility = Status.Visibility.byNum(
|
Status.Visibility replyVisibility = Status.Visibility.byNum(
|
||||||
intent.getIntExtra(REPLY_VISIBILITY_EXTRA, Status.Visibility.UNKNOWN.getNum()));
|
intent.getIntExtra(REPLY_VISIBILITY_EXTRA, Status.Visibility.UNKNOWN.getNum()));
|
||||||
|
|
||||||
if(replyVisibility != Status.Visibility.UNKNOWN) {
|
if (replyVisibility != Status.Visibility.UNKNOWN) {
|
||||||
startingVisibility = replyVisibility;
|
startingVisibility = replyVisibility;
|
||||||
} else {
|
} else {
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
startingVisibility = Status.Visibility.byString(
|
startingVisibility = Status.Visibility.byString(
|
||||||
preferences.getString("defaultPostPrivacy",
|
preferences.getString("defaultPostPrivacy",
|
||||||
Status.Visibility.PUBLIC.serverString()));
|
Status.Visibility.PUBLIC.serverString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +395,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
} else if (savedMediaQueued != null) {
|
} else if (savedMediaQueued != null) {
|
||||||
for (SavedQueuedMedia item : savedMediaQueued) {
|
for (SavedQueuedMedia item : savedMediaQueued) {
|
||||||
Bitmap preview = MediaUtils.getImageThumbnail(getContentResolver(), item.uri, THUMBNAIL_SIZE);
|
Bitmap preview = MediaUtils.getImageThumbnail(getContentResolver(), item.uri, THUMBNAIL_SIZE);
|
||||||
addMediaToQueue(item.type, preview, item.uri, item.mediaSize, item.readyStage);
|
addMediaToQueue(item.type, preview, item.uri, item.mediaSize, item.readyStage, item.description);
|
||||||
}
|
}
|
||||||
} else if (intent != null && savedInstanceState == null) {
|
} else if (intent != null && savedInstanceState == null) {
|
||||||
/* Get incoming images being sent through a share action from another app. Only do this
|
/* Get incoming images being sent through a share action from another app. Only do this
|
||||||
|
@ -451,7 +456,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
ArrayList<SavedQueuedMedia> savedMediaQueued = new ArrayList<>();
|
ArrayList<SavedQueuedMedia> savedMediaQueued = new ArrayList<>();
|
||||||
for (QueuedMedia item : mediaQueued) {
|
for (QueuedMedia item : mediaQueued) {
|
||||||
savedMediaQueued.add(new SavedQueuedMedia(item.type, item.uri,
|
savedMediaQueued.add(new SavedQueuedMedia(item.type, item.uri,
|
||||||
item.mediaSize, item.readyStage));
|
item.mediaSize, item.readyStage, item.description));
|
||||||
}
|
}
|
||||||
outState.putParcelableArrayList("savedMediaQueued", savedMediaQueued);
|
outState.putParcelableArrayList("savedMediaQueued", savedMediaQueued);
|
||||||
outState.putBoolean("showMarkSensitive", showMarkSensitive);
|
outState.putBoolean("showMarkSensitive", showMarkSensitive);
|
||||||
|
@ -797,7 +802,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
textEditor.setMimeTypes(mimeTypes,
|
textEditor.setMimeTypes(mimeTypes,
|
||||||
(inputContentInfo, flags, opts) ->
|
(inputContentInfo, flags, opts) ->
|
||||||
ComposeActivity.this.onCommitContent(inputContentInfo, flags,
|
ComposeActivity.this.onCommitContent(inputContentInfo, flags,
|
||||||
mimeTypes));
|
mimeTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
||||||
|
@ -1114,8 +1119,10 @@ public final class ComposeActivity extends BaseActivity
|
||||||
R.attr.compose_media_button_disabled_tint);
|
R.attr.compose_media_button_disabled_tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize, QueuedMedia.ReadyStage readyStage) {
|
private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize,
|
||||||
final QueuedMedia item = new QueuedMedia(type, uri, new ProgressImageView(this), mediaSize);
|
QueuedMedia.ReadyStage readyStage, @Nullable String description) {
|
||||||
|
final QueuedMedia item = new QueuedMedia(type, uri, new ProgressImageView(this),
|
||||||
|
mediaSize, description);
|
||||||
item.readyStage = readyStage;
|
item.readyStage = readyStage;
|
||||||
ImageView view = item.preview;
|
ImageView view = item.preview;
|
||||||
Resources resources = getResources();
|
Resources resources = getResources();
|
||||||
|
@ -1128,7 +1135,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
view.setLayoutParams(layoutParams);
|
view.setLayoutParams(layoutParams);
|
||||||
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
view.setImageBitmap(preview);
|
view.setImageBitmap(preview);
|
||||||
view.setOnClickListener(v -> removeMediaFromQueue(item));
|
view.setOnClickListener(v -> onMediaClick(item, v));
|
||||||
view.setContentDescription(getString(R.string.action_delete));
|
view.setContentDescription(getString(R.string.action_delete));
|
||||||
mediaPreviewBar.addView(view);
|
mediaPreviewBar.addView(view);
|
||||||
mediaQueued.add(item);
|
mediaQueued.add(item);
|
||||||
|
@ -1161,6 +1168,91 @@ public final class ComposeActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onMediaClick(QueuedMedia item, View view) {
|
||||||
|
PopupMenu popup = new PopupMenu(this, view);
|
||||||
|
final int addCaptionId = 1;
|
||||||
|
final int removeId = 2;
|
||||||
|
popup.getMenu().add(0, addCaptionId, 0, R.string.action_set_caption);
|
||||||
|
popup.getMenu().add(0, removeId, 0, R.string.action_remove_media);
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
switch (menuItem.getItemId()) {
|
||||||
|
case addCaptionId:
|
||||||
|
makeCaptionDialog(item);
|
||||||
|
break;
|
||||||
|
case removeId:
|
||||||
|
removeMediaFromQueue(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeCaptionDialog(QueuedMedia item) {
|
||||||
|
LinearLayout dialogLayout = new LinearLayout(this);
|
||||||
|
int padding = Utils.dpToPx(this, 8);
|
||||||
|
dialogLayout.setPadding(padding, padding, padding, padding);
|
||||||
|
|
||||||
|
dialogLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
ImageView imageView = new ImageView(this);
|
||||||
|
Picasso.with(this)
|
||||||
|
.load(item.uri)
|
||||||
|
.into(imageView);
|
||||||
|
|
||||||
|
int margin = Utils.dpToPx(this, 4);
|
||||||
|
dialogLayout.addView(imageView);
|
||||||
|
((LinearLayout.LayoutParams) imageView.getLayoutParams()).weight = 1;
|
||||||
|
imageView.getLayoutParams().height = 0;
|
||||||
|
((LinearLayout.LayoutParams) imageView.getLayoutParams()).setMargins(0, margin, 0, 0);
|
||||||
|
|
||||||
|
EditText input = new EditText(this);
|
||||||
|
input.setHint(R.string.hint_describe_for_visually_impaired);
|
||||||
|
dialogLayout.addView(input);
|
||||||
|
((LinearLayout.LayoutParams) input.getLayoutParams()).setMargins(margin, margin, margin, margin);
|
||||||
|
input.setLines(1);
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||||
|
input.setText(item.description);
|
||||||
|
|
||||||
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||||
|
.setView(dialogLayout)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
Window window = dialog.getWindow();
|
||||||
|
if (window != null) {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
window.setSoftInputMode(
|
||||||
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setOnShowListener(dialogInterface -> {
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(view -> mastodonApi.updateMedia(item.id,
|
||||||
|
input.getText().toString()).enqueue(new Callback<Attachment>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<Attachment> call, @NonNull Response<Attachment> response) {
|
||||||
|
Attachment attachment = response.body();
|
||||||
|
if (response.isSuccessful() && attachment != null) {
|
||||||
|
item.description = attachment.description;
|
||||||
|
dialog.dismiss();
|
||||||
|
} else {
|
||||||
|
showFailedCaptionMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<Attachment> call, @NonNull Throwable t) {
|
||||||
|
showFailedCaptionMessage();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFailedCaptionMessage() {
|
||||||
|
Toast.makeText(this, R.string.error_failed_set_caption, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
private void removeMediaFromQueue(QueuedMedia item) {
|
private void removeMediaFromQueue(QueuedMedia item) {
|
||||||
mediaPreviewBar.removeView(item.preview);
|
mediaPreviewBar.removeView(item.preview);
|
||||||
mediaQueued.remove(item);
|
mediaQueued.remove(item);
|
||||||
|
@ -1171,7 +1263,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
textEditor.setPadding(textEditor.getPaddingLeft(), textEditor.getPaddingTop(),
|
textEditor.setPadding(textEditor.getPaddingLeft(), textEditor.getPaddingTop(),
|
||||||
textEditor.getPaddingRight(), 0);
|
textEditor.getPaddingRight(), 0);
|
||||||
}
|
}
|
||||||
removeUrlFromEditable(textEditor.getEditableText(), item.uploadUrl);
|
textEditor.setText(removeUrlFromEditable(textEditor.getEditableText(), item.uploadUrl));
|
||||||
enableMediaButtons();
|
enableMediaButtons();
|
||||||
cancelReadyingMedia(item);
|
cancelReadyingMedia(item);
|
||||||
}
|
}
|
||||||
|
@ -1376,7 +1468,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
Bitmap bitmap = MediaUtils.getVideoThumbnail(this, uri, THUMBNAIL_SIZE);
|
Bitmap bitmap = MediaUtils.getVideoThumbnail(this, uri, THUMBNAIL_SIZE);
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
addMediaToQueue(QueuedMedia.Type.VIDEO, bitmap, uri, mediaSize, null);
|
addMediaToQueue(QueuedMedia.Type.VIDEO, bitmap, uri, mediaSize, null, null);
|
||||||
} else {
|
} else {
|
||||||
displayTransientError(R.string.error_media_upload_opening);
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
}
|
}
|
||||||
|
@ -1385,7 +1477,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
case "image": {
|
case "image": {
|
||||||
Bitmap bitmap = MediaUtils.getImageThumbnail(contentResolver, uri, THUMBNAIL_SIZE);
|
Bitmap bitmap = MediaUtils.getImageThumbnail(contentResolver, uri, THUMBNAIL_SIZE);
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
addMediaToQueue(QueuedMedia.Type.IMAGE, bitmap, uri, mediaSize, null);
|
addMediaToQueue(QueuedMedia.Type.IMAGE, bitmap, uri, mediaSize, null, null);
|
||||||
} else {
|
} else {
|
||||||
displayTransientError(R.string.error_media_upload_opening);
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
}
|
}
|
||||||
|
@ -1464,12 +1556,15 @@ public final class ComposeActivity extends BaseActivity
|
||||||
ReadyStage readyStage;
|
ReadyStage readyStage;
|
||||||
byte[] content;
|
byte[] content;
|
||||||
long mediaSize;
|
long mediaSize;
|
||||||
|
String description;
|
||||||
|
|
||||||
QueuedMedia(Type type, Uri uri, ProgressImageView preview, long mediaSize) {
|
QueuedMedia(Type type, Uri uri, ProgressImageView preview, long mediaSize,
|
||||||
|
String description) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.preview = preview;
|
this.preview = preview;
|
||||||
this.mediaSize = mediaSize;
|
this.mediaSize = mediaSize;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
|
@ -1502,12 +1597,14 @@ public final class ComposeActivity extends BaseActivity
|
||||||
Uri uri;
|
Uri uri;
|
||||||
long mediaSize;
|
long mediaSize;
|
||||||
QueuedMedia.ReadyStage readyStage;
|
QueuedMedia.ReadyStage readyStage;
|
||||||
|
String description;
|
||||||
|
|
||||||
SavedQueuedMedia(QueuedMedia.Type type, Uri uri, long mediaSize, QueuedMedia.ReadyStage readyStage) {
|
SavedQueuedMedia(QueuedMedia.Type type, Uri uri, long mediaSize, QueuedMedia.ReadyStage readyStage, String description) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.mediaSize = mediaSize;
|
this.mediaSize = mediaSize;
|
||||||
this.readyStage = readyStage;
|
this.readyStage = readyStage;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
SavedQueuedMedia(Parcel parcel) {
|
SavedQueuedMedia(Parcel parcel) {
|
||||||
|
@ -1515,6 +1612,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
uri = parcel.readParcelable(Uri.class.getClassLoader());
|
uri = parcel.readParcelable(Uri.class.getClassLoader());
|
||||||
mediaSize = parcel.readLong();
|
mediaSize = parcel.readLong();
|
||||||
readyStage = QueuedMedia.ReadyStage.valueOf(parcel.readString());
|
readyStage = QueuedMedia.ReadyStage.valueOf(parcel.readString());
|
||||||
|
description = parcel.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1528,6 +1626,7 @@ public final class ComposeActivity extends BaseActivity
|
||||||
dest.writeParcelable(uri, flags);
|
dest.writeParcelable(uri, flags);
|
||||||
dest.writeLong(mediaSize);
|
dest.writeLong(mediaSize);
|
||||||
dest.writeString(readyStage.name());
|
dest.writeString(readyStage.name());
|
||||||
|
dest.writeString(description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import retrofit2.http.GET;
|
||||||
import retrofit2.http.Multipart;
|
import retrofit2.http.Multipart;
|
||||||
import retrofit2.http.PATCH;
|
import retrofit2.http.PATCH;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
import retrofit2.http.Part;
|
import retrofit2.http.Part;
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
@ -88,6 +89,10 @@ public interface MastodonApi {
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("api/v1/media")
|
@POST("api/v1/media")
|
||||||
Call<Attachment> uploadMedia(@Part MultipartBody.Part file);
|
Call<Attachment> uploadMedia(@Part MultipartBody.Part file);
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT("api/v1/media/{mediaId}")
|
||||||
|
Call<Attachment> updateMedia(@Path("mediaId") String mediaId,
|
||||||
|
@Field("description") String description);
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("api/v1/statuses")
|
@POST("api/v1/statuses")
|
||||||
|
|
|
@ -275,6 +275,10 @@
|
||||||
<string name="action_lists">Lists</string>
|
<string name="action_lists">Lists</string>
|
||||||
<string name="title_lists">Lists</string>
|
<string name="title_lists">Lists</string>
|
||||||
<string name="title_list_timeline">List timeline</string>
|
<string name="title_list_timeline">List timeline</string>
|
||||||
|
<string name="error_failed_set_caption">Failed to set caption</string>
|
||||||
|
<string name="hint_describe_for_visually_impaired">Describe for visually impaired</string>
|
||||||
|
<string name="action_set_caption">Set caption</string>
|
||||||
|
<string name="action_remove_media">Remove</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Reference in a new issue