Fixed composer losing attachments and status options when changing configuration. Also, the composer remembers your last visibility choice.

This commit is contained in:
Vavassor 2017-02-17 17:56:31 -05:00
parent af4af94775
commit 9eb47a471d
3 changed files with 128 additions and 17 deletions

View file

@ -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. 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). Also, [my mastodon account is Vavassor@mastodon.social](https://mastodon.social/users/Vavassor).
## Building ## Building

View file

@ -24,10 +24,10 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils; import android.media.ThumbnailUtils;
import android.net.Uri; import android.net.Uri;
@ -35,6 +35,7 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -46,7 +47,6 @@ import android.text.Spannable;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.widget.Button; import android.widget.Button;
@ -80,6 +80,7 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
public class ComposeActivity extends BaseActivity { 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_CHARACTER_LIMIT = 500;
private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB
private static final int MEDIA_PICK_RESULT = 1; private static final int MEDIA_PICK_RESULT = 1;
@ -91,7 +92,7 @@ public class ComposeActivity extends BaseActivity {
private EditText textEditor; private EditText textEditor;
private ImageButton mediaPick; private ImageButton mediaPick;
private LinearLayout mediaPreviewBar; private LinearLayout mediaPreviewBar;
private List<QueuedMedia> mediaQueued; private ArrayList<QueuedMedia> mediaQueued;
private CountUpDownLatch waitForMediaLatch; private CountUpDownLatch waitForMediaLatch;
private boolean showMarkSensitive; private boolean showMarkSensitive;
private String statusVisibility; // The current values of the options that will be applied private String statusVisibility; // The current values of the options that will be applied
@ -107,23 +108,72 @@ public class ComposeActivity extends BaseActivity {
enum ReadyStage { enum ReadyStage {
DOWNSIZING, DOWNSIZING,
UPLOADING, UPLOADING
} }
Type type; Type type;
ImageView preview; ImageView preview;
Uri uri; Uri uri;
String id; String id;
Request uploadRequest;
ReadyStage readyStage; ReadyStage readyStage;
byte[] content; byte[] content;
long mediaSize;
QueuedMedia(Type type, Uri uri, ImageView preview) { QueuedMedia(Type type, Uri uri, ImageView preview, long mediaSize) {
this.type = type; this.type = type;
this.uri = uri; this.uri = uri;
this.preview = preview; 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<SavedQueuedMedia> CREATOR
= new Parcelable.Creator<SavedQueuedMedia>() {
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) { private void doErrorDialog(int descriptionId, int actionId, View.OnClickListener listener) {
Snackbar bar = Snackbar.make(findViewById(R.id.activity_compose), getString(descriptionId), Snackbar bar = Snackbar.make(findViewById(R.id.activity_compose), getString(descriptionId),
Snackbar.LENGTH_SHORT); Snackbar.LENGTH_SHORT);
@ -247,6 +297,24 @@ public class ComposeActivity extends BaseActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compose); setContentView(R.layout.activity_compose);
SharedPreferences preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
ArrayList<SavedQueuedMedia> 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(); Intent intent = getIntent();
String[] mentionedUsernames = null; String[] mentionedUsernames = null;
if (intent != null) { if (intent != null) {
@ -254,8 +322,6 @@ public class ComposeActivity extends BaseActivity {
mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames"); mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames");
} }
SharedPreferences preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
domain = preferences.getString("domain", null); domain = preferences.getString("domain", null);
accessToken = preferences.getString("accessToken", null); accessToken = preferences.getString("accessToken", null);
@ -358,6 +424,44 @@ public class ComposeActivity extends BaseActivity {
fragment.show(getSupportFragmentManager(), null); 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<SavedQueuedMedia> 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, private void sendStatus(String content, String visibility, boolean sensitive,
@ -373,12 +477,12 @@ public class ComposeActivity extends BaseActivity {
if (inReplyToId != null) { if (inReplyToId != null) {
parameters.put("in_reply_to_id", inReplyToId); parameters.put("in_reply_to_id", inReplyToId);
} }
JSONArray media_ids = new JSONArray(); JSONArray mediaIds = new JSONArray();
for (QueuedMedia item : mediaQueued) { for (QueuedMedia item : mediaQueued) {
media_ids.put(item.id); mediaIds.put(item.id);
} }
if (media_ids.length() > 0) { if (mediaIds.length() > 0) {
parameters.put("media_ids", media_ids); parameters.put("media_ids", mediaIds);
} }
} catch (JSONException e) { } catch (JSONException e) {
onSendFailure(); onSendFailure();
@ -531,7 +635,7 @@ public class ComposeActivity extends BaseActivity {
} }
private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize) { 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; ImageView view = item.preview;
Resources resources = getResources(); Resources resources = getResources();
int side = resources.getDimensionPixelSize(R.dimen.compose_media_preview_side); int side = resources.getDimensionPixelSize(R.dimen.compose_media_preview_side);
@ -720,7 +824,8 @@ public class ComposeActivity extends BaseActivity {
return data; return data;
} }
}; };
request.addMarker("media_" + item.uri.toString()); request.setTag(TAG);
item.uploadRequest = request;
VolleySingleton.getInstance(this).addToRequestQueue(request); VolleySingleton.getInstance(this).addToRequestQueue(request);
} }
@ -731,10 +836,14 @@ public class ComposeActivity extends BaseActivity {
private void cancelReadyingMedia(QueuedMedia item) { private void cancelReadyingMedia(QueuedMedia item) {
if (item.readyStage == QueuedMedia.ReadyStage.UPLOADING) { 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 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {

View file

@ -69,7 +69,7 @@ public class VolleySingleton {
getRequestQueue().add(request); getRequestQueue().add(request);
} }
public void cancelRequest(String tag) { public void cancelAll(String tag) {
getRequestQueue().cancelAll(tag); getRequestQueue().cancelAll(tag);
} }