Save image name and image url for uploaded images when uploading images. Uploading captured images is available.

This commit is contained in:
Alex Ning 2021-07-04 22:46:15 +08:00
parent 5210bbe7db
commit f3d905c817
8 changed files with 379 additions and 30 deletions

View File

@ -0,0 +1,7 @@
package ml.docilealligator.infinityforreddit;
public interface UploadImageEnabledActivity {
void uploadImage();
void captureImage();
void insertImageUrl(UploadedImage uploadedImage);
}

View File

@ -0,0 +1,42 @@
package ml.docilealligator.infinityforreddit;
import android.os.Parcel;
import android.os.Parcelable;
public class UploadedImage implements Parcelable {
public String imageName;
public String imageUrl;
public UploadedImage(String imageName, String imageUrl) {
this.imageName = imageName;
this.imageUrl = imageUrl;
}
protected UploadedImage(Parcel in) {
imageName = in.readString();
imageUrl = in.readString();
}
public static final Creator<UploadedImage> CREATOR = new Creator<UploadedImage>() {
@Override
public UploadedImage createFromParcel(Parcel in) {
return new UploadedImage(in);
}
@Override
public UploadedImage[] newArray(int size) {
return new UploadedImage[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(imageName);
parcel.writeString(imageUrl);
}
}

View File

@ -1,13 +1,19 @@
package ml.docilealligator.infinityforreddit.activities;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.Spanned;
import android.text.util.Linkify;
import android.view.Menu;
@ -22,6 +28,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -36,7 +43,9 @@ import org.greenrobot.eventbus.Subscribe;
import org.json.JSONException;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@ -57,8 +66,11 @@ import io.noties.markwon.recycler.table.TableEntry;
import io.noties.markwon.recycler.table.TableEntryPlugin;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.UploadImageEnabledActivity;
import ml.docilealligator.infinityforreddit.UploadedImage;
import ml.docilealligator.infinityforreddit.adapters.MarkdownBottomBarRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.CopyTextBottomSheetFragment;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.UploadedImagesBottomSheetFragment;
import ml.docilealligator.infinityforreddit.comment.Comment;
import ml.docilealligator.infinityforreddit.comment.SendComment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
@ -68,7 +80,7 @@ import ml.docilealligator.infinityforreddit.utils.UploadImageUtils;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
public class CommentActivity extends BaseActivity {
public class CommentActivity extends BaseActivity implements UploadImageEnabledActivity {
public static final String EXTRA_COMMENT_PARENT_TEXT_KEY = "ECPTK";
public static final String EXTRA_COMMENT_PARENT_TEXT_MARKDOWN_KEY = "ECPTMK";
@ -81,6 +93,8 @@ public class CommentActivity extends BaseActivity {
public static final String RETURN_EXTRA_COMMENT_DATA_KEY = "RECDK";
public static final int WRITE_COMMENT_REQUEST_CODE = 1;
private static final int PICK_IMAGE_REQUEST_CODE = 100;
private static final int CAPTURE_IMAGE_REQUEST_CODE = 200;
private static final String UPLOADED_IMAGES_STATE = "UIS";
@BindView(R.id.coordinator_layout_comment_activity)
CoordinatorLayout coordinatorLayout;
@ -121,6 +135,8 @@ public class CommentActivity extends BaseActivity {
private boolean isSubmitting = false;
private boolean isReplying;
private int markdownColor;
private Uri capturedImageUri;
private ArrayList<UploadedImage> uploadedImages = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -247,6 +263,10 @@ public class CommentActivity extends BaseActivity {
setSupportActionBar(toolbar);
if (savedInstanceState != null) {
uploadedImages = savedInstanceState.getParcelableArrayList(UPLOADED_IMAGES_STATE);
}
MarkdownBottomBarRecyclerViewAdapter adapter = new MarkdownBottomBarRecyclerViewAdapter(
mCustomThemeWrapper, new MarkdownBottomBarRecyclerViewAdapter.ItemClickListener() {
@Override
@ -257,11 +277,13 @@ public class CommentActivity extends BaseActivity {
@Override
public void onUploadImage() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
getResources().getString(R.string.select_from_gallery)), PICK_IMAGE_REQUEST_CODE);
Utils.hideKeyboard(CommentActivity.this);
UploadedImagesBottomSheetFragment fragment = new UploadedImagesBottomSheetFragment();
Bundle arguments = new Bundle();
arguments.putParcelableArrayList(UploadedImagesBottomSheetFragment.EXTRA_UPLOADED_IMAGES,
uploadedImages);
fragment.setArguments(arguments);
fragment.show(getSupportFragmentManager(), fragment.getTag());
}
});
@ -276,6 +298,12 @@ public class CommentActivity extends BaseActivity {
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList(UPLOADED_IMAGES_STATE, uploadedImages);
}
@Override
public SharedPreferences getDefaultSharedPreferences() {
return mSharedPreferences;
@ -397,34 +425,46 @@ public class CommentActivity extends BaseActivity {
Toast.makeText(CommentActivity.this, R.string.error_getting_image, Toast.LENGTH_LONG).show();
return;
}
Handler handler = new Handler();
mExecutor.execute(() -> {
try {
Bitmap bitmap = Glide.with(CommentActivity.this).asBitmap().load(data.getData()).submit().get();
String imageUrlOrError = UploadImageUtils.uploadImage(mOauthRetrofit, mUploadMediaRetrofit, mAccessToken, bitmap);
handler.post(() -> {
if (imageUrlOrError != null && !imageUrlOrError.startsWith("Error: ")) {
int start = Math.max(commentEditText.getSelectionStart(), 0);
int end = Math.max(commentEditText.getSelectionEnd(), 0);
commentEditText.getText().replace(Math.min(start, end), Math.max(start, end),
"[" + imageUrlOrError + "](" + imageUrlOrError + ")",
0, "[]()".length() + imageUrlOrError.length() + imageUrlOrError.length());
} else {
Toast.makeText(CommentActivity.this, R.string.upload_image_failed, Toast.LENGTH_LONG).show();
}
});
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(CommentActivity.this, R.string.get_image_bitmap_failed, Toast.LENGTH_LONG).show());
} catch (XmlPullParserException | JSONException | IOException e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(CommentActivity.this, R.string.error_processing_image, Toast.LENGTH_LONG).show());
}
});
uploadImageToReddit(data.getData());
} else if (requestCode == CAPTURE_IMAGE_REQUEST_CODE) {
uploadImageToReddit(capturedImageUri);
}
}
}
private void uploadImageToReddit(Uri imageUri) {
Handler handler = new Handler();
mExecutor.execute(() -> {
try {
Bitmap bitmap = Glide.with(CommentActivity.this).asBitmap().load(imageUri).submit().get();
String imageUrlOrError = UploadImageUtils.uploadImage(mOauthRetrofit, mUploadMediaRetrofit, mAccessToken, bitmap);
handler.post(() -> {
if (imageUrlOrError != null && !imageUrlOrError.startsWith("Error: ")) {
String fileName = getFileName(imageUri);
if (fileName == null) {
fileName = imageUrlOrError;
}
uploadedImages.add(new UploadedImage(fileName, imageUrlOrError));
int start = Math.max(commentEditText.getSelectionStart(), 0);
int end = Math.max(commentEditText.getSelectionEnd(), 0);
commentEditText.getText().replace(Math.min(start, end), Math.max(start, end),
"[" + imageUrlOrError + "](" + imageUrlOrError + ")",
0, "[]()".length() + imageUrlOrError.length() + imageUrlOrError.length());
} else {
Toast.makeText(CommentActivity.this, R.string.upload_image_failed, Toast.LENGTH_LONG).show();
}
});
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(CommentActivity.this, R.string.get_image_bitmap_failed, Toast.LENGTH_LONG).show());
} catch (XmlPullParserException | JSONException | IOException e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(CommentActivity.this, R.string.error_processing_image, Toast.LENGTH_LONG).show());
}
});
}
@Override
public void onBackPressed() {
if (isSubmitting) {
@ -448,4 +488,52 @@ public class CommentActivity extends BaseActivity {
public void onAccountSwitchEvent(SwitchAccountEvent event) {
finish();
}
@Nullable
private String getFileName(Uri uri) {
ContentResolver contentResolver = getContentResolver();
if (contentResolver != null) {
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor != null) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
cursor.moveToFirst();
return cursor.getString(nameIndex);
}
}
return null;
}
@Override
public void uploadImage() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
getResources().getString(R.string.select_from_gallery)), PICK_IMAGE_REQUEST_CODE);
}
@Override
public void captureImage() {
Intent pictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
capturedImageUri = FileProvider.getUriForFile(this, "ml.docilealligator.infinityforreddit.provider",
File.createTempFile("captured_image", ".jpg", getExternalFilesDir(Environment.DIRECTORY_PICTURES)));
pictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri);
startActivityForResult(pictureIntent, CAPTURE_IMAGE_REQUEST_CODE);
} catch (IOException ex) {
Toast.makeText(this, R.string.error_creating_temp_file, Toast.LENGTH_SHORT).show();
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.no_camera_available, Toast.LENGTH_SHORT).show();
}
}
@Override
public void insertImageUrl(UploadedImage uploadedImage) {
int start = Math.max(commentEditText.getSelectionStart(), 0);
int end = Math.max(commentEditText.getSelectionEnd(), 0);
commentEditText.getText().replace(Math.min(start, end), Math.max(start, end),
"[" + uploadedImage.imageName + "](" + uploadedImage.imageUrl + ")",
0, "[]()".length() + uploadedImage.imageName.length() + uploadedImage.imageUrl.length());
}
}

View File

@ -0,0 +1,61 @@
package ml.docilealligator.infinityforreddit.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.UploadedImage;
public class UploadedImagesRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<UploadedImage> uploadedImages;
private ItemClickListener itemClickListener;
public UploadedImagesRecyclerViewAdapter(ArrayList<UploadedImage> uploadedImages, ItemClickListener itemClickListener) {
this.uploadedImages = uploadedImages;
this.itemClickListener = itemClickListener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new UploadedImageViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_uploaded_image, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
((UploadedImageViewHolder) holder).imageNameTextView.setText(uploadedImages.get(position).imageName);
((UploadedImageViewHolder) holder).imageUrlTextView.setText(uploadedImages.get(position).imageUrl);
}
@Override
public int getItemCount() {
return uploadedImages == null ? 0 : uploadedImages.size();
}
private class UploadedImageViewHolder extends RecyclerView.ViewHolder {
TextView imageNameTextView;
TextView imageUrlTextView;
public UploadedImageViewHolder(@NonNull View itemView) {
super(itemView);
imageNameTextView = itemView.findViewById(R.id.image_name_item_uploaded_image);
imageUrlTextView = itemView.findViewById(R.id.image_url_item_uploaded_image);
itemView.setOnClickListener(view -> {
itemClickListener.onClick(uploadedImages.get(getBindingAdapterPosition()));
});
}
}
public interface ItemClickListener {
void onClick(UploadedImage uploadedImage);
}
}

View File

@ -0,0 +1,67 @@
package ml.docilealligator.infinityforreddit.bottomsheetfragments;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.deishelon.roundedbottomsheet.RoundedBottomSheetDialogFragment;
import com.google.android.material.button.MaterialButton;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.UploadImageEnabledActivity;
import ml.docilealligator.infinityforreddit.adapters.UploadedImagesRecyclerViewAdapter;
public class UploadedImagesBottomSheetFragment extends RoundedBottomSheetDialogFragment {
public static final String EXTRA_UPLOADED_IMAGES = "EUI";
private MaterialButton uploadButton;
private MaterialButton captureButton;
private RecyclerView uploadedImagesRecyclerView;
private UploadedImagesRecyclerViewAdapter adapter;
private UploadImageEnabledActivity activity;
public UploadedImagesBottomSheetFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_uploaded_images_bottom_sheet, container, false);
uploadButton = rootView.findViewById(R.id.upload_button_uploaded_images_bottom_sheet_fragment);
captureButton = rootView.findViewById(R.id.capture_button_uploaded_images_bottom_sheet_fragment);
uploadButton.setOnClickListener(view -> {
activity.uploadImage();
dismiss();
});
captureButton.setOnClickListener(view -> {
activity.captureImage();
dismiss();
});
uploadedImagesRecyclerView = rootView.findViewById(R.id.recycler_view_uploaded_images_bottom_sheet);
adapter = new UploadedImagesRecyclerViewAdapter(
getArguments().getParcelableArrayList(EXTRA_UPLOADED_IMAGES), uploadedImage -> {
activity.insertImageUrl(uploadedImage);
dismiss();
});
uploadedImagesRecyclerView.setAdapter(adapter);
return rootView;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.activity = (UploadImageEnabledActivity) context;
}
}

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".bottomsheetfragments.UploadedImagesBottomSheetFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="@string/uploaded_images"
android:fontFamily="?attr/font_family"
android:textSize="?attr/font_18"
android:textColor="?attr/primaryTextColor" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end">
<com.google.android.material.button.MaterialButton
android:id="@+id/capture_button_uploaded_images_bottom_sheet_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="#FFFFFF"
android:text="@string/capture" />
<com.google.android.material.button.MaterialButton
android:id="@+id/upload_button_uploaded_images_bottom_sheet_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="#FFFFFF"
android:text="@string/upload" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_uploaded_images_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/image_name_item_uploaded_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="?attr/font_family"
android:textSize="?attr/font_16"
android:textColor="?attr/primaryTextColor" />
<TextView
android:id="@+id/image_url_item_uploaded_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="?attr/font_family"
android:textSize="?attr/font_12"
android:textColor="?attr/secondaryTextColor" />
</LinearLayout>

View File

@ -1118,6 +1118,9 @@
<string name="reply">Reply</string>
<string name="uploaded_images">Uploaded Images</string>
<string name="upload">Upload</string>
<string name="capture">Capture</string>
<string name="get_image_bitmap_failed">Unable to get the bitmap of the image</string>
<string name="upload_image_failed">Unable to upload the image</string>