parent
d2a5dcc144
commit
15e37576e5
4 changed files with 215 additions and 17 deletions
|
@ -145,7 +145,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||
|
||||
if (BuildConfig.DEBUG) {
|
||||
okBuilder.addInterceptor(
|
||||
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
|
||||
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC));
|
||||
}
|
||||
|
||||
Retrofit retrofit = new Retrofit.Builder().baseUrl(getBaseUrl())
|
||||
|
|
|
@ -86,6 +86,7 @@ import com.keylesspalace.tusky.entity.Account;
|
|||
import com.keylesspalace.tusky.entity.Media;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
|
||||
import com.keylesspalace.tusky.network.ProgressRequestBody;
|
||||
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
||||
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
||||
import com.keylesspalace.tusky.util.IOUtils;
|
||||
|
@ -96,6 +97,7 @@ import com.keylesspalace.tusky.util.SpanUtils;
|
|||
import com.keylesspalace.tusky.util.StringUtils;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.keylesspalace.tusky.view.EditTextTyped;
|
||||
import com.keylesspalace.tusky.view.ProgressImageView;
|
||||
import com.keylesspalace.tusky.view.RoundedTransformation;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
|
@ -115,7 +117,6 @@ import java.util.Locale;
|
|||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
@ -317,7 +318,8 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
||||
// try to redo a list of media
|
||||
loadedDraftMediaUris = new Gson().fromJson(savedJsonUrls,
|
||||
new TypeToken<ArrayList<String>>() {}.getType());
|
||||
new TypeToken<ArrayList<String>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
int savedTootUid = intent.getIntExtra("saved_toot_uid", 0);
|
||||
|
@ -399,7 +401,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
statusAlreadyInFlight = false;
|
||||
|
||||
// These can only be added after everything affected by the media queue is initialized.
|
||||
if (!ListUtils.isEmpty(loadedDraftMediaUris)) {
|
||||
if (!ListUtils.isEmpty(loadedDraftMediaUris)) {
|
||||
for (String uriString : loadedDraftMediaUris) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
long mediaSize = MediaUtils.getMediaSize(getContentResolver(), uri);
|
||||
|
@ -650,6 +652,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
|
||||
/**
|
||||
* A∖B={x∈A|x∉B}
|
||||
*
|
||||
* @return all elements of set A that are not in set B.
|
||||
*/
|
||||
private static List<String> setDifference(List<String> a, List<String> b) {
|
||||
|
@ -672,7 +675,8 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
String savedJsonUrls = getIntent().getStringExtra("saved_json_urls");
|
||||
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
||||
existingUris = new Gson().fromJson(savedJsonUrls,
|
||||
new TypeToken<ArrayList<String>>() {}.getType());
|
||||
new TypeToken<ArrayList<String>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
final TootEntity toot = new TootEntity();
|
||||
|
@ -683,7 +687,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
if (!ListUtils.isEmpty(savedList)) {
|
||||
String json = new Gson().toJson(savedList);
|
||||
toot.setUrls(json);
|
||||
if(!ListUtils.isEmpty(existingUris)) {
|
||||
if (!ListUtils.isEmpty(existingUris)) {
|
||||
deleteMedia(setDifference(existingUris, savedList));
|
||||
}
|
||||
} else {
|
||||
|
@ -836,7 +840,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
}
|
||||
|
||||
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
||||
String[] mimeTypes) {
|
||||
String[] mimeTypes) {
|
||||
try {
|
||||
if (currentInputContentInfo != null) {
|
||||
currentInputContentInfo.releasePermission();
|
||||
|
@ -898,7 +902,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
}
|
||||
|
||||
private void sendStatus(String content, String visibility, boolean sensitive,
|
||||
String spoilerText) {
|
||||
String spoilerText) {
|
||||
ArrayList<String> mediaIds = new ArrayList<>();
|
||||
|
||||
for (QueuedMedia item : mediaQueued) {
|
||||
|
@ -1068,7 +1072,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
|
||||
@NonNull int[] grantResults) {
|
||||
@NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {
|
||||
if (grantResults.length > 0
|
||||
|
@ -1150,7 +1154,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
}
|
||||
|
||||
private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize, QueuedMedia.ReadyStage readyStage) {
|
||||
final QueuedMedia item = new QueuedMedia(type, uri, new ImageView(this), mediaSize);
|
||||
final QueuedMedia item = new QueuedMedia(type, uri, new ProgressImageView(this), mediaSize);
|
||||
item.readyStage = readyStage;
|
||||
ImageView view = item.preview;
|
||||
Resources resources = getResources();
|
||||
|
@ -1261,7 +1265,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
private void uploadMedia(final QueuedMedia item) {
|
||||
item.readyStage = QueuedMedia.ReadyStage.UPLOADING;
|
||||
|
||||
final String mimeType = getContentResolver().getType(item.uri);
|
||||
String mimeType = getContentResolver().getType(item.uri);
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
String fileExtension = map.getExtensionFromMimeType(mimeType);
|
||||
final String filename = String.format("%s_%s_%s.%s",
|
||||
|
@ -1290,14 +1294,36 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
}
|
||||
}
|
||||
|
||||
RequestBody requestFile = RequestBody.create(MediaType.parse(mimeType), content);
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", filename, requestFile);
|
||||
if (mimeType == null) mimeType = "multipart/form-data";
|
||||
|
||||
item.preview.setProgress(0);
|
||||
|
||||
ProgressRequestBody fileBody = new ProgressRequestBody(content, MediaType.parse(mimeType),
|
||||
false, // If request body logging is enabled, pass true
|
||||
new ProgressRequestBody.UploadCallback() { // may reference activity longer than I would like to
|
||||
int lastProgress = -1;
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(final int percentage) {
|
||||
if (percentage != lastProgress) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
item.preview.setProgress(percentage);
|
||||
}
|
||||
});
|
||||
}
|
||||
lastProgress = percentage;
|
||||
}
|
||||
});
|
||||
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", filename, fileBody);
|
||||
|
||||
item.uploadRequest = mastodonApi.uploadMedia(body);
|
||||
|
||||
item.uploadRequest.enqueue(new Callback<Media>() {
|
||||
@Override
|
||||
public void onResponse(Call<Media> call, retrofit2.Response<Media> response) {
|
||||
public void onResponse(@NonNull Call<Media> call, @NonNull retrofit2.Response<Media> response) {
|
||||
if (response.isSuccessful()) {
|
||||
onUploadSuccess(item, response.body());
|
||||
} else {
|
||||
|
@ -1307,7 +1333,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<Media> call, Throwable t) {
|
||||
public void onFailure(@NonNull Call<Media> call, @NonNull Throwable t) {
|
||||
Log.d(TAG, "Upload request failed. " + t.getMessage());
|
||||
onUploadFailure(item, call.isCanceled());
|
||||
}
|
||||
|
@ -1316,6 +1342,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
|
||||
private void onUploadSuccess(final QueuedMedia item, Media media) {
|
||||
item.id = media.id;
|
||||
item.preview.setProgress(-1);
|
||||
item.readyStage = QueuedMedia.ReadyStage.UPLOADED;
|
||||
|
||||
/* Add the upload URL to the text field. Also, keep a reference to the span so if the user
|
||||
|
@ -1517,7 +1544,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
|
||||
private static class QueuedMedia {
|
||||
Type type;
|
||||
ImageView preview;
|
||||
ProgressImageView preview;
|
||||
Uri uri;
|
||||
String id;
|
||||
Call<Media> uploadRequest;
|
||||
|
@ -1526,7 +1553,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||
byte[] content;
|
||||
long mediaSize;
|
||||
|
||||
QueuedMedia(Type type, Uri uri, ImageView preview, long mediaSize) {
|
||||
QueuedMedia(Type type, Uri uri, ProgressImageView preview, long mediaSize) {
|
||||
this.type = type;
|
||||
this.uri = uri;
|
||||
this.preview = preview;
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okio.BufferedSink;
|
||||
|
||||
public final class ProgressRequestBody extends RequestBody {
|
||||
private final byte[] content;
|
||||
private final UploadCallback mListener;
|
||||
private final MediaType mediaType;
|
||||
private boolean shouldIgnoreThisPass;
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 2048;
|
||||
|
||||
public interface UploadCallback {
|
||||
void onProgressUpdate(int percentage);
|
||||
}
|
||||
|
||||
public ProgressRequestBody(final byte[] content, final MediaType mediaType, boolean shouldIgnoreFirst, final UploadCallback listener) {
|
||||
this.content = content;
|
||||
this.mediaType = mediaType;
|
||||
mListener = listener;
|
||||
shouldIgnoreThisPass = shouldIgnoreFirst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return content.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
||||
long length = content.length;
|
||||
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(content);
|
||||
long uploaded = 0;
|
||||
|
||||
try {
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
if (!shouldIgnoreThisPass) {
|
||||
mListener.onProgressUpdate((int)(100 * uploaded / length));
|
||||
}
|
||||
uploaded += read;
|
||||
sink.write(buffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
shouldIgnoreThisPass = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.varunest.sparkbutton.helpers.Utils;
|
||||
|
||||
public final class ProgressImageView extends AppCompatImageView {
|
||||
|
||||
private int progress = -1;
|
||||
private RectF progressRect = new RectF();
|
||||
private RectF biggerRect = new RectF();
|
||||
private Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public ProgressImageView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public ProgressImageView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public ProgressImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
circlePaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
|
||||
circlePaint.setStrokeWidth(Utils.dpToPx(getContext(), 4));
|
||||
circlePaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
|
||||
}
|
||||
|
||||
public void setProgress(int progress) {
|
||||
this.progress = progress;
|
||||
if (progress != -1) {
|
||||
setColorFilter(Color.rgb(123, 123, 123), PorterDuff.Mode.MULTIPLY);
|
||||
} else {
|
||||
clearColorFilter();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (progress == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
float angle = (progress / 100f) * 360 - 90;
|
||||
float halfWidth = canvas.getWidth() / 2;
|
||||
float halfHeight = canvas.getHeight() / 2;
|
||||
progressRect.set(halfWidth * 0.75f, halfHeight * 0.75f, halfWidth * 1.25f, halfHeight * 1.25f);
|
||||
biggerRect.set(progressRect);
|
||||
int margin = 8;
|
||||
biggerRect.set(progressRect.left - margin, progressRect.top - margin, progressRect.right + margin, progressRect.bottom + margin);
|
||||
canvas.saveLayer(biggerRect, null, Canvas.ALL_SAVE_FLAG);
|
||||
canvas.drawOval(progressRect, circlePaint);
|
||||
canvas.drawArc(biggerRect, angle, 360 - angle - 90, true, clearPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue