diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 4efa93a2..15fefa90 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -54,7 +54,10 @@ import android.support.v7.app.ActionBar; import android.support.v7.content.res.AppCompatResources; import android.support.v7.widget.Toolbar; import android.text.Editable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextWatcher; +import android.text.style.URLSpan; import android.view.MenuItem; import android.view.View; import android.webkit.MimeTypeMap; @@ -90,6 +93,7 @@ import okhttp3.MultipartBody; import okhttp3.RequestBody; import retrofit2.Call; import retrofit2.Callback; +import retrofit2.Response; public class ComposeActivity extends BaseActivity implements ComposeOptionsFragment.Listener { private static final String TAG = "ComposeActivity"; // logging tag @@ -143,6 +147,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag Uri uri; String id; Call uploadRequest; + URLSpan uploadUrl; ReadyStage readyStage; byte[] content; long mediaSize; @@ -224,7 +229,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag floatingBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - prepareStatus(); + onSendClicked(); } }); pickBtn.setOnClickListener(new View.OnClickListener() { @@ -584,24 +589,12 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag enableButtons(); } - private void prepareStatus() { + private void onSendClicked() { if (statusAlreadyInFlight) { return; } - String contentText = textEditor.getText().toString(); - String spoilerText = ""; - if (statusHideText) { - spoilerText = contentWarningEditor.getText().toString(); - } - int characterCount = contentText.length() + spoilerText.length(); - if (characterCount > 0 && characterCount <= STATUS_CHARACTER_LIMIT) { - setStateToReadying(); - readyStatus(contentText, statusVisibility, statusMarkSensitive, spoilerText); - } else if (characterCount <= 0) { - textEditor.setError(getString(R.string.error_empty)); - } else { - textEditor.setError(getString(R.string.error_compose_character_limit)); - } + setStateToReadying(); + readyStatus(statusVisibility, statusMarkSensitive); } @Override @@ -705,7 +698,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag mastodonAPI.createStatus(content, inReplyToId, spoilerText, visibility, sensitive, mediaIds).enqueue(new Callback() { @Override - public void onResponse(Call call, retrofit2.Response response) { + public void onResponse(Call call, Response response) { if (response.isSuccessful()) { onSendSuccess(); } else { @@ -732,8 +725,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag setStateToNotReadying(); } - private void readyStatus(final String content, final String visibility, final boolean sensitive, - final String spoilerText) { + private void readyStatus(final String visibility, final boolean sensitive) { finishingUploadDialog = ProgressDialog.show( this, getString(R.string.dialog_title_finishing_media_upload), getString(R.string.dialog_message_uploading_media), true, true); @@ -755,9 +747,9 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag finishingUploadDialog.dismiss(); finishingUploadDialog = null; if (successful) { - sendStatus(content, visibility, sensitive, spoilerText); + onReadySuccess(visibility, sensitive); } else { - onReadyFailure(content, visibility, sensitive, spoilerText); + onReadyFailure(visibility, sensitive); } } @@ -780,13 +772,33 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag waitForMediaTask.execute(); } - private void onReadyFailure(final String content, final String visibility, - final boolean sensitive, final String spoilerText) { + private void onReadySuccess(String visibility, boolean sensitive) { + /* Validate the status meets the character limit. This has to be delayed until after all + * uploads finish because their links are added when the upload succeeds and that affects + * whether the limit is met or not. */ + String contentText = textEditor.getText().toString(); + String spoilerText = ""; + if (statusHideText) { + spoilerText = contentWarningEditor.getText().toString(); + } + int characterCount = contentText.length() + spoilerText.length(); + if (characterCount > 0 && characterCount <= STATUS_CHARACTER_LIMIT) { + sendStatus(contentText, visibility, sensitive, spoilerText); + } else if (characterCount <= 0) { + textEditor.setError(getString(R.string.error_empty)); + setStateToNotReadying(); + } else { + textEditor.setError(getString(R.string.error_compose_character_limit)); + setStateToNotReadying(); + } + } + + private void onReadyFailure(final String visibility, final boolean sensitive) { doErrorDialog(R.string.error_media_upload_sending, R.string.action_retry, new View.OnClickListener() { @Override public void onClick(View v) { - readyStatus(content, visibility, sensitive, spoilerText); + readyStatus(visibility, sensitive); } }); setStateToNotReadying(); @@ -951,6 +963,15 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag textEditor.setPadding(textEditor.getPaddingLeft(), textEditor.getPaddingTop(), textEditor.getPaddingRight(), 0); } + // Remove the text URL associated with this media. + if (item.uploadUrl != null) { + Editable text = textEditor.getText(); + int start = text.getSpanStart(item.uploadUrl); + int end = text.getSpanEnd(item.uploadUrl); + if (start != -1 && end != -1) { + text.delete(start, end); + } + } enableMediaButtons(); cancelReadyingMedia(item); } @@ -1052,8 +1073,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { - item.id = response.body().id; - waitForMediaLatch.countDown(); + onUploadSuccess(item, response.body()); } else { Log.d(TAG, "Upload request failed. " + response.message()); onUploadFailure(item, call.isCanceled()); @@ -1068,6 +1088,22 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag }); } + private void onUploadSuccess(final QueuedMedia item, Media media) { + item.id = media.id; + + /* Add the upload URL to the text field. Also, keep a reference to the span so if the user + * chooses to remove the media, the URL is also automatically removed. */ + item.uploadUrl = new URLSpan(media.textUrl); + int end = 1 + media.textUrl.length(); + SpannableStringBuilder builder = new SpannableStringBuilder(); + builder.append(' '); + builder.append(media.textUrl); + builder.setSpan(item.uploadUrl, 0, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + textEditor.append(builder); + + waitForMediaLatch.countDown(); + } + private void onUploadFailure(QueuedMedia item, boolean isCanceled) { if (!isCanceled) { /* if the upload was voluntarily cancelled, such as if the user clicked on it to remove