Edit and delete a file (#626)

update if file is modified on resume

implementation of edit file

Merge branch 'master' into 44-delete-file

Merge branch '44-delete-file' of codeberg.org:gitnex/GitNex into 44-delete-file

Merge branch 'master' into 44-delete-file

# Conflicts:
#	app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java
#	app/src/main/java/org/mian/gitnex/activities/FileViewActivity.java
#	app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java
#	app/src/main/java/org/mian/gitnex/fragments/BottomSheetFileViewerFragment.java

Merge branch 'master' into 44-delete-file

implement delete file

add links to bottomsheet

Co-authored-by: M M Arif <mmarif@swatian.com>
Co-authored-by: 6543 <6543@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/626
This commit is contained in:
M M Arif 2020-08-18 16:37:10 +02:00
parent 76137c56bc
commit 7caff70946
10 changed files with 468 additions and 14 deletions

View File

@ -23,6 +23,8 @@ import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Branches;
import org.mian.gitnex.models.DeleteFile;
import org.mian.gitnex.models.EditFile;
import org.mian.gitnex.models.NewFile;
import java.util.ArrayList;
import java.util.List;
@ -44,8 +46,12 @@ public class CreateFileActivity extends BaseActivity {
private EditText newFileBranchName;
private EditText newFileCommitMessage;
private Spinner newFileBranchesSpinner;
private String filePath;
private String fileSha;
private int fileAction = 0; // 0 = create, 1 = delete, 2 = edit
final Context ctx = this;
private Context appCtx;
private TinyDB tinyDb;
List<Branches> branchesList = new ArrayList<>();
@ -59,12 +65,12 @@ public class CreateFileActivity extends BaseActivity {
super.onCreate(savedInstanceState);
appCtx = getApplicationContext();
tinyDb = new TinyDB(appCtx);
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
@ -80,6 +86,8 @@ public class CreateFileActivity extends BaseActivity {
newFileCommitMessage = findViewById(R.id.newFileCommitMessage);
TextView branchNameId = findViewById(R.id.branchNameId);
TextView branchNameHintText = findViewById(R.id.branchNameHintText);
TextView toolbarTitle = findViewById(R.id.toolbarTitle);
TextView fileNameHint = findViewById(R.id.fileNameHint);
newFileName.requestFocus();
assert imm != null;
@ -90,6 +98,46 @@ public class CreateFileActivity extends BaseActivity {
newFileCreate = findViewById(R.id.newFileCreate);
if(getIntent().getStringExtra("filePath") != null && getIntent().getIntExtra("fileAction", 1) == 1) {
fileNameHint.setVisibility(View.GONE);
fileAction = getIntent().getIntExtra("fileAction", 1);
filePath = getIntent().getStringExtra("filePath");
String fileContents = getIntent().getStringExtra("fileContents");
fileSha = getIntent().getStringExtra("fileSha");
toolbarTitle.setText(getString(R.string.deleteFileText, filePath));
newFileCreate.setText(R.string.deleteFile);
newFileName.setText(filePath);
newFileName.setEnabled(false);
newFileName.setFocusable(false);
newFileContent.setText(fileContents);
newFileContent.setEnabled(false);
newFileContent.setFocusable(false);
}
if(getIntent().getStringExtra("filePath") != null && getIntent().getIntExtra("fileAction", 2) == 2) {
fileNameHint.setVisibility(View.GONE);
fileAction = getIntent().getIntExtra("fileAction", 2);
filePath = getIntent().getStringExtra("filePath");
String fileContents = getIntent().getStringExtra("fileContents");
fileSha = getIntent().getStringExtra("fileSha");
toolbarTitle.setText(getString(R.string.editFileText, filePath));
newFileCreate.setText(R.string.editFile);
newFileName.setText(filePath);
newFileName.setEnabled(false);
newFileName.setFocusable(false);
newFileContent.setText(fileContents);
}
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
@ -197,7 +245,22 @@ public class CreateFileActivity extends BaseActivity {
else {
disableProcessButton();
createNewFile(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, newFileName_, appUtil.encodeBase64(newFileContent_), newFileBranchName_, newFileCommitMessage_, currentBranch.toString());
if(fileAction == 1) {
deleteFile(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, filePath,
newFileBranchName_, newFileCommitMessage_, currentBranch.toString(), fileSha);
}
else if(fileAction == 2) {
editFile(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, filePath,
appUtil.encodeBase64(newFileContent_), newFileBranchName_, newFileCommitMessage_, currentBranch.toString(), fileSha);
}
else {
createNewFile(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, newFileName_,
appUtil.encodeBase64(newFileContent_), newFileBranchName_, newFileCommitMessage_, currentBranch.toString());
}
}
@ -256,6 +319,7 @@ public class CreateFileActivity extends BaseActivity {
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
enableProcessButton();
}
@ -263,6 +327,149 @@ public class CreateFileActivity extends BaseActivity {
}
private void deleteFile(final String instanceUrl, final String token, String repoOwner, String repoName, String fileName, String fileBranchName, String fileCommitMessage, String currentBranch, String fileSha) {
String branchName;
DeleteFile deleteFileJsonStr;
if(currentBranch.equals("No branch")) {
branchName = fileBranchName;
deleteFileJsonStr = new DeleteFile("", fileCommitMessage, fileBranchName, fileSha);
}
else {
branchName = currentBranch;
deleteFileJsonStr = new DeleteFile(currentBranch, fileCommitMessage, "", fileSha);
}
Call<JsonElement> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.deleteFile(token, repoOwner, repoName, fileName, deleteFileJsonStr);
call.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
if(response.code() == 200) {
enableProcessButton();
Toasty.info(ctx, getString(R.string.deleteFileMessage, branchName));
getIntent().removeExtra("filePath");
getIntent().removeExtra("fileSha");
getIntent().removeExtra("fileContents");
finish();
}
else if(response.code() == 401) {
enableProcessButton();
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else {
if(response.code() == 404) {
enableProcessButton();
Toasty.info(ctx, getString(R.string.apiNotFound));
}
else {
enableProcessButton();
Toasty.info(ctx, getString(R.string.genericError));
}
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
enableProcessButton();
}
});
}
private void editFile(final String instanceUrl, final String token, String repoOwner, String repoName, String fileName, String fileContent, String fileBranchName, String fileCommitMessage, String currentBranch, String fileSha) {
String branchName;
EditFile editFileJsonStr;
if(currentBranch.equals("No branch")) {
branchName = fileBranchName;
editFileJsonStr = new EditFile("", fileCommitMessage, fileBranchName, fileSha, fileContent);
}
else {
branchName = currentBranch;
editFileJsonStr = new EditFile(currentBranch, fileCommitMessage, "", fileSha, fileContent);
}
Call<JsonElement> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.editFile(token, repoOwner, repoName, fileName, editFileJsonStr);
call.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
if(response.code() == 200) {
enableProcessButton();
Toasty.info(ctx, getString(R.string.editFileMessage, branchName));
getIntent().removeExtra("filePath");
getIntent().removeExtra("fileSha");
getIntent().removeExtra("fileContents");
tinyDb.putBoolean("fileModified", true);
finish();
}
else if(response.code() == 401) {
enableProcessButton();
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else {
if(response.code() == 404) {
enableProcessButton();
Toasty.info(ctx, getString(R.string.apiNotFound));
}
else {
enableProcessButton();
Toasty.info(ctx, getString(R.string.genericError));
}
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
enableProcessButton();
}
});
}
private void getBranches(String instanceUrl, String instanceToken, String repoOwner, String repoName, String loginUid) {
Call<List<Branches>> call = RetrofitClient

View File

@ -84,6 +84,7 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
private byte[] decodedPdf;
private Boolean pdfNightMode;
private String singleFileName;
private String fileSha;
private AppUtil appUtil;
private TinyDB tinyDb;
@ -153,6 +154,27 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
}
@Override
public void onResume() {
super.onResume();
String repoFullName = tinyDb.getString("repoFullName");
String repoBranch = tinyDb.getString("repoBranch");
String[] parts = repoFullName.split("/");
String repoOwner = parts[0];
String repoName = parts[1];
String instanceUrl = tinyDb.getString("instanceUrl");
String loginUid = tinyDb.getString("loginUid");
String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
if(tinyDb.getBoolean("fileModified")) {
getSingleFileContents(instanceUrl, instanceToken, repoOwner, repoName, singleFileName, repoBranch);
tinyDb.putBoolean("fileModified", false);
}
}
private void getSingleFileContents(String instanceUrl, String token, final String owner, String repo, final String filename, String ref) {
Call<Files> call = RetrofitClient.getInstance(instanceUrl, ctx).getApiInterface().getSingleFileContents(token, owner, repo, filename, ref);
@ -171,6 +193,8 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
String fileExtension = FileUtils.getExtension(filename);
mProgressBar.setVisibility(View.GONE);
fileSha = response.body().getSha();
// download file meta
tinyDb.putString("downloadFileName", filename);
tinyDb.putString("downloadFileContents", response.body().getContent());
@ -403,7 +427,42 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
if("downloadFile".equals(text)) {
requestFileDownload();
}
if("deleteFile".equals(text)) {
String fileExtension = FileUtils.getExtension(singleFileName);
String data = appUtil.decodeBase64(tinyDb.getString("downloadFileContents"));
Intent intent = new Intent(ctx, CreateFileActivity.class);
intent.putExtra("fileAction", 1);
intent.putExtra("filePath", singleFileName);
intent.putExtra("fileSha", fileSha);
if(!appUtil.imageExtension(fileExtension)) {
intent.putExtra("fileContents", data);
}
else {
intent.putExtra("fileContents", "");
}
ctx.startActivity(intent);
}
if("editFile".equals(text)) {
String fileExtension = FileUtils.getExtension(singleFileName);
String data = appUtil.decodeBase64(tinyDb.getString("downloadFileContents"));
Intent intent = new Intent(ctx, CreateFileActivity.class);
intent.putExtra("fileAction", 2);
intent.putExtra("filePath", singleFileName);
intent.putExtra("fileSha", fileSha);
if(!appUtil.imageExtension(fileExtension)) {
intent.putExtra("fileContents", data);
}
else {
intent.putExtra("fileContents", "");
}
ctx.startActivity(intent);
}
}
@ -426,6 +485,7 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
}
else {
Toasty.warning(ctx, getString(R.string.waitLoadingDownloadFile));
}
@ -456,6 +516,7 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
}
catch(IOException e) {
Log.e("errorFileDownloading", Objects.requireNonNull(e.getMessage()));
}
@ -469,7 +530,6 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie
getIntent().removeExtra("singleFileName");
finish();
};
}

View File

@ -26,15 +26,27 @@ public class BottomSheetFileViewerFragment extends BottomSheetDialogFragment {
View v = inflater.inflate(R.layout.bottom_sheet_file_viewer, container, false);
TextView downloadFile = v.findViewById(R.id.downloadFile);
TextView deleteFile = v.findViewById(R.id.deleteFile);
TextView editFile = v.findViewById(R.id.editFile);
downloadFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bmListener.onButtonClicked("downloadFile");
dismiss();
}
downloadFile.setOnClickListener(v1 -> {
bmListener.onButtonClicked("downloadFile");
dismiss();
});
deleteFile.setOnClickListener(v1 -> {
bmListener.onButtonClicked("deleteFile");
dismiss();
});
editFile.setOnClickListener(v1 -> {
bmListener.onButtonClicked("editFile");
dismiss();
});
return v;
}
@ -47,9 +59,11 @@ public class BottomSheetFileViewerFragment extends BottomSheetDialogFragment {
super.onAttach(context);
try {
bmListener = (BottomSheetFileViewerFragment.BottomSheetListener) context;
}
catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}

View File

@ -7,6 +7,8 @@ import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Commits;
import org.mian.gitnex.models.CreateIssue;
import org.mian.gitnex.models.CreateLabel;
import org.mian.gitnex.models.DeleteFile;
import org.mian.gitnex.models.EditFile;
import org.mian.gitnex.models.Emails;
import org.mian.gitnex.models.ExploreRepositories;
import org.mian.gitnex.models.Files;
@ -39,6 +41,7 @@ import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.Header;
import retrofit2.http.PATCH;
import retrofit2.http.POST;
@ -265,6 +268,12 @@ public interface ApiInterface {
@GET("repos/{owner}/{repo}/contents/{fileDir}") // get all the sub files and dirs of a repository
Call<List<Files>> getDirFiles(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("fileDir") String fileDir, @Query("ref") String ref);
@HTTP(method = "DELETE", path = "repos/{owner}/{repo}/contents/{filepath}", hasBody = true) // delete a file
Call<JsonElement> deleteFile(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("filepath") String filepath, @Body DeleteFile jsonStr);
@PUT("repos/{owner}/{repo}/contents/{filepath}") // edit/update a file
Call<JsonElement> editFile(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Path("filepath") String filepath, @Body EditFile jsonStr);
@GET("user/starred/{owner}/{repo}") // check star status of a repository
Call<JsonElement> checkRepoStarStatus(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName);

View File

@ -0,0 +1,60 @@
package org.mian.gitnex.models;
/**
* Author M M Arif
*/
public class DeleteFile {
private String branch;
private String message;
private String new_branch;
private String sha;
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getNew_branch() {
return new_branch;
}
public void setNew_branch(String new_branch) {
this.new_branch = new_branch;
}
public String getSha() {
return sha;
}
public void setSha(String sha) {
this.sha = sha;
}
public DeleteFile(String branch, String message, String new_branch, String sha) {
this.branch = branch;
this.message = message;
this.new_branch = new_branch;
this.sha = sha;
}
}

View File

@ -0,0 +1,73 @@
package org.mian.gitnex.models;
/**
* Author M M Arif
*/
public class EditFile {
private String branch;
private String message;
private String new_branch;
private String sha;
private String content;
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getNew_branch() {
return new_branch;
}
public void setNew_branch(String new_branch) {
this.new_branch = new_branch;
}
public String getSha() {
return sha;
}
public void setSha(String sha) {
this.sha = sha;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public EditFile(String branch, String message, String new_branch, String sha, String content) {
this.branch = branch;
this.message = message;
this.new_branch = new_branch;
this.sha = sha;
this.content = content;
}
}

View File

@ -46,7 +46,7 @@ public class NewFile {
this.new_branch = new_branch;
}
public class authorObject {
public static class authorObject {
private String email;
private String name;
@ -68,7 +68,7 @@ public class NewFile {
}
}
public class committerObject {
public static class committerObject {
private String email;
private String name;
@ -96,4 +96,4 @@ public class NewFile {
this.message = message;
this.new_branch = new_branch;
}
}
}

View File

@ -30,7 +30,7 @@
android:src="@drawable/ic_close" />
<TextView
android:id="@+id/toolbar_title"
android:id="@+id/toolbarTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@ -78,6 +78,7 @@
android:inputType="textCapSentences|text" />
<TextView
android:id="@+id/fileNameHint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newFileNameHintMessage"
@ -214,7 +215,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/characters255Limit"
android:textColor="?attr/primaryTextColor"
android:textColor="?attr/hintColor"
android:textSize="12sp"
android:paddingStart="10dp"
android:paddingEnd="5dp"

View File

@ -17,6 +17,30 @@
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="@+id/editFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/editFile"
android:drawableStart="@drawable/ic_edit"
android:drawablePadding="24dp"
android:padding="12dp"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
<TextView
android:id="@+id/deleteFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/deleteFile"
android:drawableStart="@drawable/ic_delete"
android:drawablePadding="24dp"
android:padding="12dp"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
<TextView
android:id="@+id/downloadFile"
android:layout_width="match_parent"

View File

@ -566,6 +566,12 @@
<string name="waitLoadingDownloadFile">Please wait for the file to load to memory</string>
<string name="downloadFileSaved">File saved successfully</string>
<string name="excludeFilesInFileviewer">This file type is not supported in file viewer. Download it instead from the three dotted menu?</string>
<string name="deleteFile">Delete This File</string>
<string name="editFile">Edit This File</string>
<string name="deleteFileText">Delete %1$s</string>
<string name="deleteFileMessage">File is set for deletion by branch %1$s</string>
<string name="editFileText">Edit %1$s</string>
<string name="editFileMessage">File is modified by branch %1$s</string>
<string name="sizeCopy">Size</string>
<string name="shareIssue">Share Issue</string>