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; package ml.docilealligator.infinityforreddit.activities;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.Spanned; import android.text.Spanned;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.view.Menu; import android.view.Menu;
@ -22,6 +28,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -36,7 +43,9 @@ import org.greenrobot.eventbus.Subscribe;
import org.json.JSONException; import org.json.JSONException;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -57,8 +66,11 @@ import io.noties.markwon.recycler.table.TableEntry;
import io.noties.markwon.recycler.table.TableEntryPlugin; import io.noties.markwon.recycler.table.TableEntryPlugin;
import ml.docilealligator.infinityforreddit.Infinity; import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R; 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.adapters.MarkdownBottomBarRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.CopyTextBottomSheetFragment; import ml.docilealligator.infinityforreddit.bottomsheetfragments.CopyTextBottomSheetFragment;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.UploadedImagesBottomSheetFragment;
import ml.docilealligator.infinityforreddit.comment.Comment; import ml.docilealligator.infinityforreddit.comment.Comment;
import ml.docilealligator.infinityforreddit.comment.SendComment; import ml.docilealligator.infinityforreddit.comment.SendComment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
@ -68,7 +80,7 @@ import ml.docilealligator.infinityforreddit.utils.UploadImageUtils;
import ml.docilealligator.infinityforreddit.utils.Utils; import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit; 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_KEY = "ECPTK";
public static final String EXTRA_COMMENT_PARENT_TEXT_MARKDOWN_KEY = "ECPTMK"; 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 String RETURN_EXTRA_COMMENT_DATA_KEY = "RECDK";
public static final int WRITE_COMMENT_REQUEST_CODE = 1; public static final int WRITE_COMMENT_REQUEST_CODE = 1;
private static final int PICK_IMAGE_REQUEST_CODE = 100; 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) @BindView(R.id.coordinator_layout_comment_activity)
CoordinatorLayout coordinatorLayout; CoordinatorLayout coordinatorLayout;
@ -121,6 +135,8 @@ public class CommentActivity extends BaseActivity {
private boolean isSubmitting = false; private boolean isSubmitting = false;
private boolean isReplying; private boolean isReplying;
private int markdownColor; private int markdownColor;
private Uri capturedImageUri;
private ArrayList<UploadedImage> uploadedImages = new ArrayList<>();
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -247,6 +263,10 @@ public class CommentActivity extends BaseActivity {
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
if (savedInstanceState != null) {
uploadedImages = savedInstanceState.getParcelableArrayList(UPLOADED_IMAGES_STATE);
}
MarkdownBottomBarRecyclerViewAdapter adapter = new MarkdownBottomBarRecyclerViewAdapter( MarkdownBottomBarRecyclerViewAdapter adapter = new MarkdownBottomBarRecyclerViewAdapter(
mCustomThemeWrapper, new MarkdownBottomBarRecyclerViewAdapter.ItemClickListener() { mCustomThemeWrapper, new MarkdownBottomBarRecyclerViewAdapter.ItemClickListener() {
@Override @Override
@ -257,11 +277,13 @@ public class CommentActivity extends BaseActivity {
@Override @Override
public void onUploadImage() { public void onUploadImage() {
Intent intent = new Intent(); Utils.hideKeyboard(CommentActivity.this);
intent.setType("image/*"); UploadedImagesBottomSheetFragment fragment = new UploadedImagesBottomSheetFragment();
intent.setAction(Intent.ACTION_GET_CONTENT); Bundle arguments = new Bundle();
startActivityForResult(Intent.createChooser(intent, arguments.putParcelableArrayList(UploadedImagesBottomSheetFragment.EXTRA_UPLOADED_IMAGES,
getResources().getString(R.string.select_from_gallery)), PICK_IMAGE_REQUEST_CODE); 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 @Override
public SharedPreferences getDefaultSharedPreferences() { public SharedPreferences getDefaultSharedPreferences() {
return mSharedPreferences; return mSharedPreferences;
@ -397,13 +425,27 @@ public class CommentActivity extends BaseActivity {
Toast.makeText(CommentActivity.this, R.string.error_getting_image, Toast.LENGTH_LONG).show(); Toast.makeText(CommentActivity.this, R.string.error_getting_image, Toast.LENGTH_LONG).show();
return; return;
} }
uploadImageToReddit(data.getData());
} else if (requestCode == CAPTURE_IMAGE_REQUEST_CODE) {
uploadImageToReddit(capturedImageUri);
}
}
}
private void uploadImageToReddit(Uri imageUri) {
Handler handler = new Handler(); Handler handler = new Handler();
mExecutor.execute(() -> { mExecutor.execute(() -> {
try { try {
Bitmap bitmap = Glide.with(CommentActivity.this).asBitmap().load(data.getData()).submit().get(); Bitmap bitmap = Glide.with(CommentActivity.this).asBitmap().load(imageUri).submit().get();
String imageUrlOrError = UploadImageUtils.uploadImage(mOauthRetrofit, mUploadMediaRetrofit, mAccessToken, bitmap); String imageUrlOrError = UploadImageUtils.uploadImage(mOauthRetrofit, mUploadMediaRetrofit, mAccessToken, bitmap);
handler.post(() -> { handler.post(() -> {
if (imageUrlOrError != null && !imageUrlOrError.startsWith("Error: ")) { 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 start = Math.max(commentEditText.getSelectionStart(), 0);
int end = Math.max(commentEditText.getSelectionEnd(), 0); int end = Math.max(commentEditText.getSelectionEnd(), 0);
commentEditText.getText().replace(Math.min(start, end), Math.max(start, end), commentEditText.getText().replace(Math.min(start, end), Math.max(start, end),
@ -422,8 +464,6 @@ public class CommentActivity extends BaseActivity {
} }
}); });
} }
}
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
@ -448,4 +488,52 @@ public class CommentActivity extends BaseActivity {
public void onAccountSwitchEvent(SwitchAccountEvent event) { public void onAccountSwitchEvent(SwitchAccountEvent event) {
finish(); 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="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="get_image_bitmap_failed">Unable to get the bitmap of the image</string>
<string name="upload_image_failed">Unable to upload the image</string> <string name="upload_image_failed">Unable to upload the image</string>