parent
d2a5dcc144
commit
15e37576e5
@ -145,7 +145,7 @@ public class BaseActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
okBuilder.addInterceptor(
|
okBuilder.addInterceptor(
|
||||||
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
|
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC));
|
||||||
}
|
}
|
||||||
|
|
||||||
Retrofit retrofit = new Retrofit.Builder().baseUrl(getBaseUrl())
|
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.Media;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
|
import com.keylesspalace.tusky.fragment.ComposeOptionsFragment;
|
||||||
|
import com.keylesspalace.tusky.network.ProgressRequestBody;
|
||||||
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
||||||
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
||||||
import com.keylesspalace.tusky.util.IOUtils;
|
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.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.RoundedTransformation;
|
import com.keylesspalace.tusky.view.RoundedTransformation;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
import com.squareup.picasso.Target;
|
import com.squareup.picasso.Target;
|
||||||
@ -115,7 +117,6 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
@ -317,7 +318,8 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
||||||
// try to redo a list of media
|
// try to redo a list of media
|
||||||
loadedDraftMediaUris = new Gson().fromJson(savedJsonUrls,
|
loadedDraftMediaUris = new Gson().fromJson(savedJsonUrls,
|
||||||
new TypeToken<ArrayList<String>>() {}.getType());
|
new TypeToken<ArrayList<String>>() {
|
||||||
|
}.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
int savedTootUid = intent.getIntExtra("saved_toot_uid", 0);
|
int savedTootUid = intent.getIntExtra("saved_toot_uid", 0);
|
||||||
@ -399,7 +401,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
statusAlreadyInFlight = false;
|
statusAlreadyInFlight = false;
|
||||||
|
|
||||||
// These can only be added after everything affected by the media queue is initialized.
|
// 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) {
|
for (String uriString : loadedDraftMediaUris) {
|
||||||
Uri uri = Uri.parse(uriString);
|
Uri uri = Uri.parse(uriString);
|
||||||
long mediaSize = MediaUtils.getMediaSize(getContentResolver(), uri);
|
long mediaSize = MediaUtils.getMediaSize(getContentResolver(), uri);
|
||||||
@ -650,6 +652,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A∖B={x∈A|x∉B}
|
* A∖B={x∈A|x∉B}
|
||||||
|
*
|
||||||
* @return all elements of set A that are not in set B.
|
* @return all elements of set A that are not in set B.
|
||||||
*/
|
*/
|
||||||
private static List<String> setDifference(List<String> a, List<String> 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");
|
String savedJsonUrls = getIntent().getStringExtra("saved_json_urls");
|
||||||
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
||||||
existingUris = new Gson().fromJson(savedJsonUrls,
|
existingUris = new Gson().fromJson(savedJsonUrls,
|
||||||
new TypeToken<ArrayList<String>>() {}.getType());
|
new TypeToken<ArrayList<String>>() {
|
||||||
|
}.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
final TootEntity toot = new TootEntity();
|
final TootEntity toot = new TootEntity();
|
||||||
@ -683,7 +687,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
if (!ListUtils.isEmpty(savedList)) {
|
if (!ListUtils.isEmpty(savedList)) {
|
||||||
String json = new Gson().toJson(savedList);
|
String json = new Gson().toJson(savedList);
|
||||||
toot.setUrls(json);
|
toot.setUrls(json);
|
||||||
if(!ListUtils.isEmpty(existingUris)) {
|
if (!ListUtils.isEmpty(existingUris)) {
|
||||||
deleteMedia(setDifference(existingUris, savedList));
|
deleteMedia(setDifference(existingUris, savedList));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -836,7 +840,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
||||||
String[] mimeTypes) {
|
String[] mimeTypes) {
|
||||||
try {
|
try {
|
||||||
if (currentInputContentInfo != null) {
|
if (currentInputContentInfo != null) {
|
||||||
currentInputContentInfo.releasePermission();
|
currentInputContentInfo.releasePermission();
|
||||||
@ -898,7 +902,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendStatus(String content, String visibility, boolean sensitive,
|
private void sendStatus(String content, String visibility, boolean sensitive,
|
||||||
String spoilerText) {
|
String spoilerText) {
|
||||||
ArrayList<String> mediaIds = new ArrayList<>();
|
ArrayList<String> mediaIds = new ArrayList<>();
|
||||||
|
|
||||||
for (QueuedMedia item : mediaQueued) {
|
for (QueuedMedia item : mediaQueued) {
|
||||||
@ -1068,7 +1072,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
|
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
|
||||||
@NonNull int[] grantResults) {
|
@NonNull int[] grantResults) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {
|
case PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {
|
||||||
if (grantResults.length > 0
|
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) {
|
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;
|
item.readyStage = readyStage;
|
||||||
ImageView view = item.preview;
|
ImageView view = item.preview;
|
||||||
Resources resources = getResources();
|
Resources resources = getResources();
|
||||||
@ -1261,7 +1265,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
private void uploadMedia(final QueuedMedia item) {
|
private void uploadMedia(final QueuedMedia item) {
|
||||||
item.readyStage = QueuedMedia.ReadyStage.UPLOADING;
|
item.readyStage = QueuedMedia.ReadyStage.UPLOADING;
|
||||||
|
|
||||||
final String mimeType = getContentResolver().getType(item.uri);
|
String mimeType = getContentResolver().getType(item.uri);
|
||||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||||
String fileExtension = map.getExtensionFromMimeType(mimeType);
|
String fileExtension = map.getExtensionFromMimeType(mimeType);
|
||||||
final String filename = String.format("%s_%s_%s.%s",
|
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);
|
if (mimeType == null) mimeType = "multipart/form-data";
|
||||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", filename, requestFile);
|
|
||||||
|
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 = mastodonApi.uploadMedia(body);
|
||||||
|
|
||||||
item.uploadRequest.enqueue(new Callback<Media>() {
|
item.uploadRequest.enqueue(new Callback<Media>() {
|
||||||
@Override
|
@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()) {
|
if (response.isSuccessful()) {
|
||||||
onUploadSuccess(item, response.body());
|
onUploadSuccess(item, response.body());
|
||||||
} else {
|
} else {
|
||||||
@ -1307,7 +1333,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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());
|
Log.d(TAG, "Upload request failed. " + t.getMessage());
|
||||||
onUploadFailure(item, call.isCanceled());
|
onUploadFailure(item, call.isCanceled());
|
||||||
}
|
}
|
||||||
@ -1316,6 +1342,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
|
|
||||||
private void onUploadSuccess(final QueuedMedia item, Media media) {
|
private void onUploadSuccess(final QueuedMedia item, Media media) {
|
||||||
item.id = media.id;
|
item.id = media.id;
|
||||||
|
item.preview.setProgress(-1);
|
||||||
item.readyStage = QueuedMedia.ReadyStage.UPLOADED;
|
item.readyStage = QueuedMedia.ReadyStage.UPLOADED;
|
||||||
|
|
||||||
/* Add the upload URL to the text field. Also, keep a reference to the span so if the user
|
/* 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 {
|
private static class QueuedMedia {
|
||||||
Type type;
|
Type type;
|
||||||
ImageView preview;
|
ProgressImageView preview;
|
||||||
Uri uri;
|
Uri uri;
|
||||||
String id;
|
String id;
|
||||||
Call<Media> uploadRequest;
|
Call<Media> uploadRequest;
|
||||||
@ -1526,7 +1553,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
|
|||||||
byte[] content;
|
byte[] content;
|
||||||
long mediaSize;
|
long mediaSize;
|
||||||
|
|
||||||
QueuedMedia(Type type, Uri uri, ImageView preview, long mediaSize) {
|
QueuedMedia(Type type, Uri uri, ProgressImageView preview, long mediaSize) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.preview = preview;
|
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…
x
Reference in New Issue
Block a user