From 9eb47a471d2d5d7998c799332acdf5674de18c74 Mon Sep 17 00:00:00 2001 From: Vavassor Date: Fri, 17 Feb 2017 17:56:31 -0500 Subject: [PATCH] Fixed composer losing attachments and status options when changing configuration. Also, the composer remembers your last visibility choice. --- README.md | 2 + .../keylesspalace/tusky/ComposeActivity.java | 141 ++++++++++++++++-- .../keylesspalace/tusky/VolleySingleton.java | 2 +- 3 files changed, 128 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7751215b..a129162a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This is an android client for [Mastodon, a GNU Social-compatible microblogging server](https://mastodon.social). Presently, it is in active development and its current state does not represent the features or design of the final program. +It is currently available for alpha testing on the [Tusky Google Play store page](https://play.google.com/store/apps/details?id=com.keylesspalace.tusky). + Also, [my mastodon account is Vavassor@mastodon.social](https://mastodon.social/users/Vavassor). ## Building diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index d9157818..41ef87fa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -24,10 +24,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; import android.media.MediaMetadataRetriever; import android.media.ThumbnailUtils; import android.net.Uri; @@ -35,6 +35,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Parcel; +import android.os.Parcelable; import android.provider.OpenableColumns; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -46,7 +47,6 @@ import android.text.Spannable; import android.text.Spanned; import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; -import android.util.TypedValue; import android.view.View; import android.webkit.MimeTypeMap; import android.widget.Button; @@ -80,6 +80,7 @@ import java.util.Map; import java.util.Random; public class ComposeActivity extends BaseActivity { + private static final String TAG = "ComposeActivity"; // logging tag, and volley request tag private static final int STATUS_CHARACTER_LIMIT = 500; private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB private static final int MEDIA_PICK_RESULT = 1; @@ -91,7 +92,7 @@ public class ComposeActivity extends BaseActivity { private EditText textEditor; private ImageButton mediaPick; private LinearLayout mediaPreviewBar; - private List mediaQueued; + private ArrayList mediaQueued; private CountUpDownLatch waitForMediaLatch; private boolean showMarkSensitive; private String statusVisibility; // The current values of the options that will be applied @@ -107,23 +108,72 @@ public class ComposeActivity extends BaseActivity { enum ReadyStage { DOWNSIZING, - UPLOADING, + UPLOADING } Type type; ImageView preview; Uri uri; String id; + Request uploadRequest; ReadyStage readyStage; byte[] content; + long mediaSize; - QueuedMedia(Type type, Uri uri, ImageView preview) { + QueuedMedia(Type type, Uri uri, ImageView preview, long mediaSize) { this.type = type; this.uri = uri; this.preview = preview; + this.mediaSize = mediaSize; } } + /**This saves enough information to re-enqueue an attachment when restoring the activity. */ + private static class SavedQueuedMedia implements Parcelable { + QueuedMedia.Type type; + Uri uri; + Bitmap preview; + long mediaSize; + + SavedQueuedMedia(QueuedMedia.Type type, Uri uri, ImageView view, long mediaSize) { + this.type = type; + this.uri = uri; + this.preview = ((BitmapDrawable) view.getDrawable()).getBitmap(); + this.mediaSize = mediaSize; + } + + SavedQueuedMedia(Parcel parcel) { + type = (QueuedMedia.Type) parcel.readSerializable(); + uri = parcel.readParcelable(Uri.class.getClassLoader()); + preview = parcel.readParcelable(Bitmap.class.getClassLoader()); + mediaSize = parcel.readLong(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeSerializable(type); + dest.writeParcelable(uri, flags); + dest.writeParcelable(preview, flags); + dest.writeLong(mediaSize); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SavedQueuedMedia createFromParcel(Parcel in) { + return new SavedQueuedMedia(in); + } + + public SavedQueuedMedia[] newArray(int size) { + return new SavedQueuedMedia[size]; + } + }; + } + private void doErrorDialog(int descriptionId, int actionId, View.OnClickListener listener) { Snackbar bar = Snackbar.make(findViewById(R.id.activity_compose), getString(descriptionId), Snackbar.LENGTH_SHORT); @@ -247,6 +297,24 @@ public class ComposeActivity extends BaseActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_compose); + SharedPreferences preferences = getSharedPreferences( + getString(R.string.preferences_file_key), Context.MODE_PRIVATE); + + ArrayList savedMediaQueued = null; + if (savedInstanceState != null) { + showMarkSensitive = savedInstanceState.getBoolean("showMarkSensitive"); + statusVisibility = savedInstanceState.getString("statusVisibility"); + statusMarkSensitive = savedInstanceState.getBoolean("statusMarkSensitive"); + statusHideText = savedInstanceState.getBoolean("statusHideText"); + // Keep these until everything needed to put them in the queue is finished initializing. + savedMediaQueued = savedInstanceState.getParcelableArrayList("savedMediaQueued"); + } else { + showMarkSensitive = false; + statusVisibility = preferences.getString("rememberedVisibility", "public"); + statusMarkSensitive = false; + statusHideText = false; + } + Intent intent = getIntent(); String[] mentionedUsernames = null; if (intent != null) { @@ -254,8 +322,6 @@ public class ComposeActivity extends BaseActivity { mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames"); } - SharedPreferences preferences = getSharedPreferences( - getString(R.string.preferences_file_key), Context.MODE_PRIVATE); domain = preferences.getString("domain", null); accessToken = preferences.getString("accessToken", null); @@ -358,10 +424,48 @@ public class ComposeActivity extends BaseActivity { fragment.show(getSupportFragmentManager(), null); } }); + + // These can only be added after everything affected by the media queue is initialized. + if (savedMediaQueued != null) { + for (SavedQueuedMedia item : savedMediaQueued) { + addMediaToQueue(item.type, item.preview, item.uri, item.mediaSize); + } + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + ArrayList savedMediaQueued = new ArrayList<>(); + for (QueuedMedia item : mediaQueued) { + savedMediaQueued.add(new SavedQueuedMedia(item.type, item.uri, item.preview, + item.mediaSize)); + } + outState.putParcelableArrayList("savedMediaQueued", savedMediaQueued); + outState.putBoolean("showMarkSensitive", showMarkSensitive); + outState.putString("statusVisibility", statusVisibility); + outState.putBoolean("statusMarkSensitive", statusMarkSensitive); + outState.putBoolean("statusHideText", statusHideText); + super.onSaveInstanceState(outState); + } + + @Override + protected void onPause() { + super.onPause(); + SharedPreferences preferences = getSharedPreferences( + getString(R.string.preferences_file_key), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("rememberedVisibility", statusVisibility); + editor.apply(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + VolleySingleton.getInstance(this).cancelAll(TAG); } private void sendStatus(String content, String visibility, boolean sensitive, - String spoilerText) { + String spoilerText) { String endpoint = getString(R.string.endpoint_status); String url = "https://" + domain + endpoint; JSONObject parameters = new JSONObject(); @@ -373,12 +477,12 @@ public class ComposeActivity extends BaseActivity { if (inReplyToId != null) { parameters.put("in_reply_to_id", inReplyToId); } - JSONArray media_ids = new JSONArray(); + JSONArray mediaIds = new JSONArray(); for (QueuedMedia item : mediaQueued) { - media_ids.put(item.id); + mediaIds.put(item.id); } - if (media_ids.length() > 0) { - parameters.put("media_ids", media_ids); + if (mediaIds.length() > 0) { + parameters.put("media_ids", mediaIds); } } catch (JSONException e) { onSendFailure(); @@ -531,7 +635,7 @@ public class ComposeActivity extends BaseActivity { } private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize) { - final QueuedMedia item = new QueuedMedia(type, uri, new ImageView(this)); + final QueuedMedia item = new QueuedMedia(type, uri, new ImageView(this), mediaSize); ImageView view = item.preview; Resources resources = getResources(); int side = resources.getDimensionPixelSize(R.dimen.compose_media_preview_side); @@ -720,7 +824,8 @@ public class ComposeActivity extends BaseActivity { return data; } }; - request.addMarker("media_" + item.uri.toString()); + request.setTag(TAG); + item.uploadRequest = request; VolleySingleton.getInstance(this).addToRequestQueue(request); } @@ -731,9 +836,13 @@ public class ComposeActivity extends BaseActivity { private void cancelReadyingMedia(QueuedMedia item) { if (item.readyStage == QueuedMedia.ReadyStage.UPLOADING) { - VolleySingleton.getInstance(this).cancelRequest("media_" + item.uri.toString()); + item.uploadRequest.cancel(); + } + if (item.id == null) { + /* The presence of an upload id is used to detect if it finished uploading or not, to + * prevent counting down twice on the same media item. */ + waitForMediaLatch.countDown(); } - waitForMediaLatch.countDown(); } @Override diff --git a/app/src/main/java/com/keylesspalace/tusky/VolleySingleton.java b/app/src/main/java/com/keylesspalace/tusky/VolleySingleton.java index 2376f5ab..c0bb215e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/VolleySingleton.java +++ b/app/src/main/java/com/keylesspalace/tusky/VolleySingleton.java @@ -69,7 +69,7 @@ public class VolleySingleton { getRequestQueue().add(request); } - public void cancelRequest(String tag) { + public void cancelAll(String tag) { getRequestQueue().cancelAll(tag); }