Compose: upload progress, post progress/error

This commit is contained in:
Grishka
2022-03-16 22:27:48 +03:00
parent 8c5d6cd4a6
commit 6ecd1cfe68
4 changed files with 101 additions and 23 deletions

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.fragments; package org.joinmastodon.android.fragments;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
@@ -7,6 +8,7 @@ import android.content.ClipData;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Outline; import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
import android.icu.text.BreakIterator; import android.icu.text.BreakIterator;
import android.net.Uri; import android.net.Uri;
@@ -16,14 +18,18 @@ import android.os.Parcelable;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewOutlineProvider; import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
@@ -143,6 +149,10 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
private String pollDurationStr; private String pollDurationStr;
private EditText spoilerEdit; private EditText spoilerEdit;
private boolean hasSpoiler; private boolean hasSpoiler;
private ProgressBar sendProgress;
private ImageView sendError;
private View sendingOverlay;
private WindowManager wm;
@Override @Override
public void onCreate(Bundle savedInstanceState){ public void onCreate(Bundle savedInstanceState){
@@ -165,6 +175,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
public void onAttach(Activity activity){ public void onAttach(Activity activity){
super.onAttach(activity); super.onAttach(activity);
setHasOptionsMenu(true); setHasOptionsMenu(true);
wm=activity.getSystemService(WindowManager.class);
} }
@Override @Override
@@ -359,8 +370,25 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
publishButton=new Button(getActivity()); publishButton=new Button(getActivity());
publishButton.setText(R.string.publish); publishButton.setText(R.string.publish);
publishButton.setOnClickListener(this::onPublishClick); publishButton.setOnClickListener(this::onPublishClick);
FrameLayout wrap=new FrameLayout(getActivity()); LinearLayout wrap=new LinearLayout(getActivity());
wrap.addView(publishButton, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP|Gravity.LEFT)); wrap.setOrientation(LinearLayout.HORIZONTAL);
sendProgress=new ProgressBar(getActivity());
LinearLayout.LayoutParams progressLP=new LinearLayout.LayoutParams(V.dp(24), V.dp(24));
progressLP.setMarginEnd(V.dp(16));
progressLP.gravity=Gravity.CENTER_VERTICAL;
wrap.addView(sendProgress, progressLP);
sendError=new ImageView(getActivity());
sendError.setImageResource(R.drawable.ic_fluent_error_circle_24_regular);
sendError.setImageTintList(getResources().getColorStateList(R.color.error_600));
sendError.setScaleType(ImageView.ScaleType.CENTER);
wrap.addView(sendError, progressLP);
sendError.setVisibility(View.GONE);
sendProgress.setVisibility(View.GONE);
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8)); wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
wrap.setClipToPadding(false); wrap.setClipToPadding(false);
MenuItem item=menu.add(R.string.publish); MenuItem item=menu.add(R.string.publish);
@@ -444,15 +472,26 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
} }
if(uuid==null) if(uuid==null)
uuid=UUID.randomUUID().toString(); uuid=UUID.randomUUID().toString();
ProgressDialog progress=new ProgressDialog(getActivity());
progress.setMessage(getString(R.string.publishing)); sendingOverlay=new View(getActivity());
progress.setCancelable(false); WindowManager.LayoutParams overlayParams=new WindowManager.LayoutParams();
progress.show(); overlayParams.type=WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
overlayParams.flags=WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
overlayParams.width=overlayParams.height=WindowManager.LayoutParams.MATCH_PARENT;
overlayParams.format=PixelFormat.TRANSLUCENT;
overlayParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
overlayParams.token=mainEditText.getWindowToken();
wm.addView(sendingOverlay, overlayParams);
publishButton.setEnabled(false);
sendProgress.setVisibility(View.VISIBLE);
sendError.setVisibility(View.GONE);
new CreateStatus(req, uuid) new CreateStatus(req, uuid)
.setCallback(new Callback<>(){ .setCallback(new Callback<>(){
@Override @Override
public void onSuccess(Status result){ public void onSuccess(Status result){
progress.dismiss(); wm.removeView(sendingOverlay);
Nav.finish(ComposeFragment.this); Nav.finish(ComposeFragment.this);
E.post(new StatusCreatedEvent(result)); E.post(new StatusCreatedEvent(result));
if(replyTo!=null){ if(replyTo!=null){
@@ -463,7 +502,10 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
@Override @Override
public void onError(ErrorResponse error){ public void onError(ErrorResponse error){
progress.dismiss(); wm.removeView(sendingOverlay);
sendProgress.setVisibility(View.GONE);
sendError.setVisibility(View.VISIBLE);
publishButton.setEnabled(true);
error.showToast(getActivity()); error.showToast(getActivity());
} }
}) })
@@ -557,6 +599,9 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
attachmentsView.addView(createMediaAttachmentView(draft)); attachmentsView.addView(createMediaAttachmentView(draft));
allAttachments.add(draft); allAttachments.add(draft);
attachmentsView.setVisibility(View.VISIBLE); attachmentsView.setVisibility(View.VISIBLE);
draft.overlay.setVisibility(View.VISIBLE);
draft.infoBar.setVisibility(View.GONE);
if(uploadingAttachment==null){ if(uploadingAttachment==null){
uploadMediaAttachment(draft); uploadMediaAttachment(draft);
}else{ }else{
@@ -577,7 +622,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
draft.view=thumb; draft.view=thumb;
draft.progressBar=thumb.findViewById(R.id.progress); draft.progressBar=thumb.findViewById(R.id.progress);
draft.infoBar=thumb.findViewById(R.id.info_bar); draft.infoBar=thumb.findViewById(R.id.info_bar);
draft.errorOverlay=thumb.findViewById(R.id.error_overlay); draft.overlay=thumb.findViewById(R.id.overlay);
draft.descriptionView=thumb.findViewById(R.id.description); draft.descriptionView=thumb.findViewById(R.id.description);
ImageButton btn=thumb.findViewById(R.id.remove_btn); ImageButton btn=thumb.findViewById(R.id.remove_btn);
btn.setTag(draft); btn.setTag(draft);
@@ -588,6 +633,8 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
Button retry=thumb.findViewById(R.id.retry_upload); Button retry=thumb.findViewById(R.id.retry_upload);
retry.setTag(draft); retry.setTag(draft);
retry.setOnClickListener(this::onRetryMediaUploadClick); retry.setOnClickListener(this::onRetryMediaUploadClick);
retry.setVisibility(View.GONE);
draft.retryButton=retry;
draft.infoBar.setTag(draft); draft.infoBar.setTag(draft);
draft.infoBar.setOnClickListener(this::onEditMediaDescriptionClick); draft.infoBar.setOnClickListener(this::onEditMediaDescriptionClick);
@@ -599,7 +646,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
} }
if(failedAttachments.contains(draft)){ if(failedAttachments.contains(draft)){
draft.infoBar.setVisibility(View.GONE); draft.infoBar.setVisibility(View.GONE);
draft.errorOverlay.setVisibility(View.VISIBLE); draft.overlay.setVisibility(View.VISIBLE);
} }
return thumb; return thumb;
@@ -609,6 +656,12 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
if(uploadingAttachment!=null) if(uploadingAttachment!=null)
throw new IllegalStateException("there is already an attachment being uploaded"); throw new IllegalStateException("there is already an attachment being uploaded");
uploadingAttachment=attachment; uploadingAttachment=attachment;
attachment.progressBar.setVisibility(View.VISIBLE);
ObjectAnimator rotationAnimator=ObjectAnimator.ofFloat(attachment.progressBar, View.ROTATION, 0f, 360f);
rotationAnimator.setInterpolator(new LinearInterpolator());
rotationAnimator.setDuration(1500);
rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE);
rotationAnimator.start();
attachment.uploadRequest=(UploadAttachment) new UploadAttachment(attachment.uri) attachment.uploadRequest=(UploadAttachment) new UploadAttachment(attachment.uri)
.setProgressListener(new ProgressListener(){ .setProgressListener(new ProgressListener(){
@Override @Override
@@ -631,6 +684,10 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
if(!queuedAttachments.isEmpty()) if(!queuedAttachments.isEmpty())
uploadMediaAttachment(queuedAttachments.remove(0)); uploadMediaAttachment(queuedAttachments.remove(0));
updatePublishButtonState(); updatePublishButtonState();
rotationAnimator.cancel();
V.setVisibilityAnimated(attachment.overlay, View.GONE);
V.setVisibilityAnimated(attachment.infoBar, View.VISIBLE);
} }
@Override @Override
@@ -641,8 +698,9 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
// error.showToast(getActivity()); // error.showToast(getActivity());
Toast.makeText(getActivity(), R.string.image_upload_failed, Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), R.string.image_upload_failed, Toast.LENGTH_SHORT).show();
V.setVisibilityAnimated(attachment.errorOverlay, View.VISIBLE); rotationAnimator.cancel();
V.setVisibilityAnimated(attachment.infoBar, View.GONE); V.setVisibilityAnimated(attachment.retryButton, View.VISIBLE);
V.setVisibilityAnimated(attachment.progressBar, View.GONE);
if(!queuedAttachments.isEmpty()) if(!queuedAttachments.isEmpty())
uploadMediaAttachment(queuedAttachments.remove(0)); uploadMediaAttachment(queuedAttachments.remove(0));
@@ -676,8 +734,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
private void onRetryMediaUploadClick(View v){ private void onRetryMediaUploadClick(View v){
DraftMediaAttachment att=(DraftMediaAttachment) v.getTag(); DraftMediaAttachment att=(DraftMediaAttachment) v.getTag();
if(failedAttachments.remove(att)){ if(failedAttachments.remove(att)){
V.setVisibilityAnimated(att.errorOverlay, View.GONE); V.setVisibilityAnimated(att.retryButton, View.GONE);
V.setVisibilityAnimated(att.infoBar, View.VISIBLE);
V.setVisibilityAnimated(att.progressBar, View.VISIBLE); V.setVisibilityAnimated(att.progressBar, View.VISIBLE);
if(uploadingAttachment==null) if(uploadingAttachment==null)
uploadMediaAttachment(att); uploadMediaAttachment(att);
@@ -803,8 +860,9 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
public transient View view; public transient View view;
public transient ProgressBar progressBar; public transient ProgressBar progressBar;
public transient TextView descriptionView; public transient TextView descriptionView;
public transient View errorOverlay; public transient View overlay;
public transient View infoBar; public transient View infoBar;
public transient Button retryButton;
} }
private static class DraftPollOption{ private static class DraftPollOption{

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 2c5.523 0 10 4.478 10 10s-4.477 10-10 10S2 17.522 2 12 6.477 2 12 2zm0 1.667c-4.595 0-8.333 3.738-8.333 8.333 0 4.595 3.738 8.333 8.333 8.333 4.595 0 8.333-3.738 8.333-8.333 0-4.595-3.738-8.333-8.333-8.333zm-0.001 10.835c0.551 0 0.998 0.447 0.998 0.999 0 0.551-0.447 0.999-0.998 0.999-0.552 0-0.999-0.448-0.999-1 0-0.55 0.447-0.998 0.999-0.998zM11.994 7c0.38 0 0.694 0.282 0.744 0.648l0.007 0.101 0.004 4.502c0 0.414-0.335 0.75-0.75 0.75-0.38 0-0.694-0.281-0.743-0.647l-0.007-0.102-0.004-4.501c0-0.415 0.335-0.75 0.75-0.751z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/progress">
<shape
android:innerRadius="18dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="true">
<solid android:color="?android:colorAccent"/>
</shape>
</item>
</layer-list>

View File

@@ -11,13 +11,6 @@
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:src="#0f0"/> tools:src="#0f0"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
style="?android:progressBarStyleHorizontal"/>
<RelativeLayout <RelativeLayout
android:id="@+id/info_bar" android:id="@+id/info_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -68,15 +61,27 @@
</RelativeLayout> </RelativeLayout>
<FrameLayout <FrameLayout
android:id="@+id/error_overlay" android:id="@+id/overlay"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#cc000000" android:background="#cc000000"
android:backgroundTint="?colorWindowBackground" android:backgroundTint="?colorWindowBackground"
android:padding="8dp" android:padding="8dp"
android:clipToPadding="false" android:clipToPadding="false"
tools:visibility="visible"
android:visibility="gone"> android:visibility="gone">
<ProgressBar
android:id="@+id/progress"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_gravity="center"
android:progressDrawable="@drawable/upload_progress"
android:max="1000"
android:padding="0dp"
android:indeterminateOnly="false"
android:indeterminate="false"/>
<Button <Button
android:id="@+id/retry_upload" android:id="@+id/retry_upload"
android:layout_width="wrap_content" android:layout_width="wrap_content"