Upload files when creating an issue..

This commit is contained in:
M M Arif 2023-09-29 22:53:22 +05:00
parent 431583841d
commit 525d8bba99
11 changed files with 643 additions and 15 deletions

View File

@ -1,17 +1,28 @@
package org.mian.gitnex.activities;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.vdurmont.emoji.EmojiParser;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
@ -21,6 +32,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import org.gitnex.tea4j.v2.models.Attachment;
import org.gitnex.tea4j.v2.models.CreateIssueOption;
import org.gitnex.tea4j.v2.models.Issue;
import org.gitnex.tea4j.v2.models.Label;
@ -30,9 +44,11 @@ import org.mian.gitnex.R;
import org.mian.gitnex.actions.AssigneesActions;
import org.mian.gitnex.actions.LabelsActions;
import org.mian.gitnex.adapters.AssigneesListAdapter;
import org.mian.gitnex.adapters.AttachmentsAdapter;
import org.mian.gitnex.adapters.LabelsListAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.ActivityCreateIssueBinding;
import org.mian.gitnex.databinding.BottomSheetAttachmentsBinding;
import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding;
import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding;
import org.mian.gitnex.fragments.IssuesFragment;
@ -40,7 +56,10 @@ import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Constants;
import org.mian.gitnex.helpers.Markdown;
import org.mian.gitnex.helpers.SnackBar;
import org.mian.gitnex.helpers.attachments.AttachmentUtils;
import org.mian.gitnex.helpers.attachments.AttachmentsModel;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import org.mian.gitnex.structs.BottomSheetListener;
import retrofit2.Call;
import retrofit2.Callback;
@ -49,7 +68,9 @@ import retrofit2.Callback;
*/
public class CreateIssueActivity extends BaseActivity
implements LabelsListAdapter.LabelsListAdapterListener,
AssigneesListAdapter.AssigneesListAdapterListener {
AssigneesListAdapter.AssigneesListAdapterListener,
BottomSheetListener,
AttachmentsAdapter.AttachmentsReceiverListener {
private final List<Label> labelsList = new ArrayList<>();
private final LinkedHashMap<String, Milestone> milestonesList = new LinkedHashMap<>();
@ -64,6 +85,9 @@ public class CreateIssueActivity extends BaseActivity
private List<String> assigneesListData = new ArrayList<>();
private boolean renderMd = false;
private RepositoryContext repositoryContext;
private static List<AttachmentsModel> attachmentsList;
private AttachmentsAdapter attachmentsAdapter;
private static final List<Uri> contentUri = new ArrayList<>();
@SuppressLint("ClickableViewAccessibility")
@Override
@ -83,6 +107,11 @@ public class CreateIssueActivity extends BaseActivity
int resultLimit = Constants.getCurrentResultLimit(ctx);
attachmentsList = new ArrayList<>();
attachmentsAdapter = new AttachmentsAdapter(attachmentsList, ctx);
AttachmentsAdapter.setAttachmentsReceiveListener(this);
viewBinding.newIssueDescription.setOnTouchListener(
(touchView, motionEvent) -> {
touchView.getParent().requestDisallowInterceptTouchEvent(true);
@ -103,11 +132,13 @@ public class CreateIssueActivity extends BaseActivity
showDatePickerDialog();
viewBinding.newIssueDueDateLayout.setEndIconOnClickListener(
view -> {
viewBinding.newIssueDueDate.setText("");
});
view -> viewBinding.newIssueDueDate.setText(""));
viewBinding.topAppBar.setNavigationOnClickListener(v -> finish());
viewBinding.topAppBar.setNavigationOnClickListener(
v -> {
finish();
contentUri.clear();
});
viewBinding.topAppBar.setOnMenuItemClickListener(
menuItem -> {
@ -139,6 +170,9 @@ public class CreateIssueActivity extends BaseActivity
} else if (id == R.id.create) {
processNewIssue();
return true;
} else if (id == R.id.attachment) {
checkForAttachments();
return true;
} else {
return super.onOptionsItemSelected(menuItem);
}
@ -159,6 +193,154 @@ public class CreateIssueActivity extends BaseActivity
}
}
ActivityResultLauncher<Intent> startActivityForResult =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
assert data != null;
contentUri.add(data.getData());
attachmentsList.add(
new AttachmentsModel(
AttachmentUtils.queryName(ctx, data.getData()),
data.getData()));
attachmentsAdapter.updateList(attachmentsList);
}
});
public void onDestroy() {
AttachmentsAdapter.setAttachmentsReceiveListener(null);
super.onDestroy();
}
@Override
public void setAttachmentsData(Uri filename) {
contentUri.remove(filename);
}
public class BottomSheetAttachments extends BottomSheetDialogFragment {
private BottomSheetListener bmListener;
@Nullable @Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
BottomSheetAttachmentsBinding bottomSheetAttachmentsBinding =
BottomSheetAttachmentsBinding.inflate(inflater, container, false);
bottomSheetAttachmentsBinding.addAttachment.setOnClickListener(
v1 -> bmListener.onButtonClicked("addAttachment"));
bottomSheetAttachmentsBinding.recyclerViewAttachments.setHasFixedSize(true);
bottomSheetAttachmentsBinding.recyclerViewAttachments.setLayoutManager(
new LinearLayoutManager(getContext()));
bottomSheetAttachmentsBinding.recyclerViewAttachments.setAdapter(attachmentsAdapter);
return bottomSheetAttachmentsBinding.getRoot();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
bmListener = (BottomSheetListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement BottomSheetListener");
}
}
}
@Override
public void onButtonClicked(String text) {
if ("addAttachment".equals(text)) {
openFileAttachmentActivity();
}
}
private void checkForAttachments() {
if (contentUri.size() > 0) {
BottomSheetAttachments bottomSheet = new BottomSheetAttachments();
bottomSheet.show(getSupportFragmentManager(), "attachmentsBottomSheet");
} else {
openFileAttachmentActivity();
}
}
private void openFileAttachmentActivity() {
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
data.addCategory(Intent.CATEGORY_OPENABLE);
data.setType("*/*");
Intent intent = Intent.createChooser(data, "Choose a file");
startActivityForResult.launch(intent);
}
private void processAttachments(long issueIndex) {
for (int i = 0; i < attachmentsAdapter.getItemCount(); i++) {
File file = AttachmentUtils.getFile(ctx, contentUri.get(i));
RequestBody requestFile =
RequestBody.create(
file, MediaType.parse(getContentResolver().getType(contentUri.get(i))));
uploadAttachments(requestFile, issueIndex, file.getName());
}
}
private void uploadAttachments(RequestBody requestFile, long issueIndex, String filename1) {
Call<Attachment> call3 =
RetrofitClient.getApiInterface(ctx)
.issueCreateIssueAttachment(
requestFile,
repository.getOwner(),
repository.getName(),
issueIndex,
filename1);
call3.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<Attachment> call,
@NonNull retrofit2.Response<Attachment> response2) {
if (response2.code() == 201) {
new Handler().postDelayed(() -> finish(), 3000);
} else if (response2.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx);
} else {
SnackBar.error(
ctx,
findViewById(android.R.id.content),
getString(R.string.attachmentsSaveError));
}
}
@Override
public void onFailure(@NonNull Call<Attachment> call, @NonNull Throwable t) {
SnackBar.error(
ctx,
findViewById(android.R.id.content),
getString(R.string.genericServerResponseError));
}
});
}
private void showDatePickerDialog() {
MaterialDatePicker.Builder<Long> builder = MaterialDatePicker.Builder.datePicker();
@ -324,7 +506,14 @@ public class CreateIssueActivity extends BaseActivity
findViewById(android.R.id.content),
getString(R.string.issueCreated));
new Handler().postDelayed(() -> finish(), 3000);
assert response2.body() != null;
if (contentUri.size() > 0) {
processAttachments(response2.body().getNumber());
contentUri.clear();
} else {
new Handler().postDelayed(() -> finish(), 3000);
}
} else if (response2.code() == 401) {

View File

@ -0,0 +1,125 @@
package org.mian.gitnex.adapters;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.PicassoService;
import org.mian.gitnex.helpers.attachments.AttachmentsModel;
/**
* @author M M Arif
*/
public class AttachmentsAdapter extends RecyclerView.Adapter<AttachmentsAdapter.ViewHolder> {
private static AttachmentsReceiverListener AttachmentsReceiveListener;
private List<AttachmentsModel> attachmentsList;
private final Context ctx;
public AttachmentsAdapter(List<AttachmentsModel> attachmentsList, Context ctx) {
this.attachmentsList = attachmentsList;
this.ctx = ctx;
}
@Override
public int getItemCount() {
return attachmentsList == null ? 0 : attachmentsList.size();
}
@NonNull @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_attachments, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
AttachmentsModel currentItem = attachmentsList.get(position);
holder.attachmentsModel = currentItem;
holder.filename.setText(currentItem.getFileName());
if (Arrays.asList("bmp", "gif", "jpg", "jpeg", "png", "webp", "heic", "heif")
.contains(FilenameUtils.getExtension(currentItem.getFileName()).toLowerCase())) {
holder.attachmentViewFrame.setVisibility(View.VISIBLE);
PicassoService.getInstance(ctx)
.get()
.load(currentItem.getUri())
.placeholder(R.drawable.loader_animated)
.resize(120, 120)
.centerCrop()
.error(R.drawable.ic_close)
.into(holder.attachment);
} else {
holder.attachmentViewFrame.setVisibility(View.GONE);
}
}
class ViewHolder extends RecyclerView.ViewHolder {
public TextView filename;
public ImageView delete;
public MaterialCardView attachmentViewFrame;
public ImageView attachment;
private AttachmentsModel attachmentsModel;
public ViewHolder(View itemView) {
super(itemView);
filename = itemView.findViewById(R.id.filename);
delete = itemView.findViewById(R.id.delete_attachment);
attachmentViewFrame = itemView.findViewById(R.id.attachmentViewFrame);
attachment = itemView.findViewById(R.id.attachment);
delete.setOnClickListener(
itemDelete -> {
AttachmentsReceiveListener.setAttachmentsData(attachmentsModel.getUri());
deleteAttachment(getBindingAdapterPosition());
notifyDataChanged();
});
}
}
@SuppressLint("NotifyDataSetChanged")
public void notifyDataChanged() {
notifyDataSetChanged();
}
public void updateList(List<AttachmentsModel> list) {
attachmentsList = list;
notifyDataChanged();
}
private void deleteAttachment(int position) {
attachmentsList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, attachmentsList.size());
}
public interface AttachmentsReceiverListener {
void setAttachmentsData(Uri myData);
}
public static void setAttachmentsReceiveListener(
AttachmentsReceiverListener attachmentsListener) {
AttachmentsReceiveListener = attachmentsListener;
}
}

View File

@ -15,7 +15,7 @@ public class SnackBar {
Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
View sbView = snackBar.getView();
TextView textView = sbView.findViewById(R.id.snackbar_text);
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral0));
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral10));
textView.setTextColor(context.getColor(R.color.colorWhite));
snackBar.show();
}
@ -24,7 +24,7 @@ public class SnackBar {
Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
View sbView = snackBar.getView();
TextView textView = sbView.findViewById(R.id.snackbar_text);
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral0));
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral10));
textView.setTextColor(context.getColor(R.color.colorWhite));
snackBar.show();
}
@ -33,8 +33,8 @@ public class SnackBar {
Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
View sbView = snackBar.getView();
TextView textView = sbView.findViewById(R.id.snackbar_text);
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral0));
textView.setTextColor(context.getColor(R.color.lightYellow));
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral10));
textView.setTextColor(context.getColor(R.color.warningColor));
snackBar.show();
}
@ -42,8 +42,8 @@ public class SnackBar {
Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
View sbView = snackBar.getView();
TextView textView = sbView.findViewById(R.id.snackbar_text);
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral0));
textView.setTextColor(context.getColor(R.color.materialcolorpicker__red));
snackBar.setBackgroundTint(context.getColor(R.color.material_dynamic_neutral10));
textView.setTextColor(context.getColor(R.color.darkRed));
snackBar.show();
}
}

View File

@ -0,0 +1,60 @@
package org.mian.gitnex.helpers.attachments;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author M M Arif
*/
public class AttachmentUtils {
public static File getFile(Context ctx, Uri uri) {
File destinationFilename;
try {
destinationFilename =
new File(
ctx.getFilesDir().getPath() + File.separatorChar + queryName(ctx, uri));
} catch (AssertionError e) {
destinationFilename = new File(uri.getPath());
}
try (InputStream ins = ctx.getContentResolver().openInputStream(uri)) {
createFileFromStream(ins, destinationFilename);
} catch (Exception ex) {
ex.printStackTrace();
}
return destinationFilename;
}
public static String queryName(Context ctx, Uri uri) {
Cursor returnCursor = ctx.getContentResolver().query(uri, null, null, null, null);
assert returnCursor != null;
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String name = returnCursor.getString(nameIndex);
returnCursor.close();
return name;
}
public static void createFileFromStream(InputStream ins, File destination) {
try (OutputStream os = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
int length;
while ((length = ins.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,42 @@
package org.mian.gitnex.helpers.attachments;
import android.net.Uri;
/**
* @author M M Arif
*/
public class AttachmentsModel {
private Uri uri;
private String fileName;
private long fileSize;
public AttachmentsModel(String fileName, Uri uri) {
this.fileName = fileName;
this.uri = uri;
}
public Uri getUri() {
return uri;
}
public void setUri(Uri uri) {
this.uri = uri;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
}

View File

@ -0,0 +1,13 @@
<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="m21.44,11.05 l-9.19,9.19a6,6 0,0 1,-8.49 -8.49l8.57,-8.57A4,4 0,1 1,18 8.84l-8.59,8.57a2,2 0,0 1,-2.83 -2.83l8.49,-8.48"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
</vector>

View File

@ -27,7 +27,7 @@
android:layout_height="?attr/actionBarSize"
app:title="@string/pageTitleCreateNewIssue"
app:layout_collapseMode="pin"
app:menu="@menu/create_menu"
app:menu="@menu/create_issue_menu"
app:navigationIcon="@drawable/ic_close" />
</com.google.android.material.appbar.CollapsingToolbarLayout>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical"
android:paddingTop="@dimen/dimen4dp"
android:paddingBottom="@dimen/dimen12dp">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dimen12dp"
android:paddingEnd="@dimen/dimen12dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/issuesAttachmentsHeadFrame"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:padding="@dimen/dimen8dp">
<TextView
android:id="@+id/bottomSheetHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="@string/attachments"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp" />
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle"
android:layout_width="@dimen/dimen28dp"
android:layout_height="@dimen/dimen4dp"
android:layout_gravity="start"
android:layout_marginTop="@dimen/dimen8dp"
android:layout_marginBottom="@dimen/dimen16dp"
app:cardCornerRadius="@dimen/dimen24dp"
app:cardElevation="@dimen/dimen0dp">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/fabColor" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
app:cardCornerRadius="@dimen/dimen24dp"
android:layout_gravity="end"
android:layout_marginStart="@dimen/dimen4dp"
android:layout_marginEnd="@dimen/dimen4dp"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/fabColor"
android:padding="@dimen/dimen8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/add_attachment"
android:layout_width="@dimen/dimen24dp"
android:layout_height="@dimen/dimen24dp"
android:src="@drawable/ic_add"
android:contentDescription="@string/generalImgContentText"
app:tint="?attr/materialCardBackgroundColor" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_attachments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen4dp"
android:padding="@dimen/dimen2dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fileFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dimen4dp"
android:paddingBottom="@dimen/dimen4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/dimen12dp"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:id="@+id/attachmentViewFrame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
android:visibility="gone"
android:layout_marginEnd="@dimen/dimen8dp"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/materialCardBackgroundColor"
android:orientation="horizontal">
<ImageView
android:id="@+id/attachment"
android:layout_width="@dimen/dimen36dp"
android:layout_height="@dimen/dimen36dp"
android:contentDescription="@string/generalImgContentText" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/filename_section"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".90"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/dimen48dp"
android:orientation="vertical">
<TextView
android:id="@+id/filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/defaultFilename"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen14sp" />
</LinearLayout>
<ImageView
android:id="@+id/delete_attachment"
android:layout_width="@dimen/dimen20dp"
android:layout_height="@dimen/dimen20dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dimen0dp"
android:contentDescription="@string/generalImgContentText"
android:src="@drawable/ic_delete" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

View File

@ -3,17 +3,24 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/attachment"
android:icon="@drawable/ic_attachment"
android:orderInCategory="0"
android:title="@string/attachment"
app:showAsAction="ifRoom" />
<item
android:id="@+id/markdown"
android:icon="@drawable/ic_markdown"
android:orderInCategory="0"
android:orderInCategory="1"
android:title="@string/strMarkdown"
app:showAsAction="ifRoom" />
<item
android:id="@+id/create"
android:title="@string/newCreateButtonCopy"
android:orderInCategory="1"
android:orderInCategory="2"
android:contentDescription="@string/newCreateButtonCopy"
app:showAsAction="ifRoom" />

View File

@ -875,4 +875,8 @@
<string name="lang_percentage" translatable="false">%s%%</string>
<string name="dashboard">Dashboard</string>
<string name="attachment">Attachment</string>
<string name="attachments">Attachments</string>
<string name="attachmentsSaveError">An issue was created but cannot process attachments at this time. Check the server logs for more details.</string>
</resources>