Skip to content

Commit

Permalink
v1.3.1 (#81)
Browse files Browse the repository at this point in the history
* Layout improvements

* Recreate view after theme changes

* Option to export config file

* Add Firebase Crashlytics

* Firebase Cloud Msg for app updates notifications

* Add option to delete remotes

* Add option to delete remotes

* Layout improvements

* Recreate view after theme changes

* Option to export config file

* Add Firebase Crashlytics

* Firebase Cloud Msg for app updates notifications

* Add option to delete remotes

* Upload only one file at a time

* Download one file at a time

* Move/delete each file individually

View is refreshed after each file is moved or deleted

* Option to empty trash for supported remotes

* Update app version
  • Loading branch information
patrykcoding authored May 26, 2018
1 parent fe2e944 commit 5eae41d
Show file tree
Hide file tree
Showing 14 changed files with 232 additions and 269 deletions.
Binary file modified .idea/caches/build_file_checksums.ser
Binary file not shown.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ Grab the [latest version](https://github.com/kaczmarkiewiczp/rcloneExplorer/rele

Credits/Libraries
-----------------
- [Android Support Libraries](https://developer.android.com/topic/libraries/support-library)
- [Floating Action Button SpeedDial](https://github.com/leinardi/FloatingActionButtonSpeedDial) - A Floating Action Button Speed Dial implementation for Android that follows the Material Design specification.
- [Glide](https://github.com/bumptech/glide) - An image loading and caching library for Android focused on smooth scrolling.
- [Markdown View](https://github.com/falnatsheh/MarkdownView) - MarkdownView is an Android webview with the capablity of loading Markdown text or file and display it as HTML, it uses MarkdownJ and extends Android webview.
- [Material Design Icons](https://github.com/Templarian/MaterialDesign) - 2200+ Material Design Icons from the Community.
- [rclone](https://github.com/ncw/rclone) - "rsync for cloud storage"
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "ca.pkay.rcloneexplorer"
minSdkVersion 21
targetSdkVersion 27
versionCode 16
versionName "1.3.0"
versionCode 17
versionName "1.3.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/assets/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
### 1.3.1
* **New:** Option to delete remotes
* **New:** Option to export rclone config file
* **New:** Option to empty trash for remotes that support it
* Encrypted remotes (crypt) are not supported
* **New:** Firebase Crashlytics
* Any app crashes will be reported to help fix them faster
* No identifiable information is sent, only line of code on which the crash occurred
* **New:** Push notifications when new app version is available on GitHub (can be disabled in settings)
* **Fix:** Recreate view after theme changes
* **Update:** Upload only one file at a time (fix any possible bottlenecks)
* **Update:** Download only one file at a time (fix any possible bottlenecks)
* **Update:** Move/delete operations - view is updated after each file is moved or deleted

***

### 1.3.0
* **New:** Remote creation - ability to create new remotes right from the app!
* Most of the rclone remotes are here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ private void createData() {
libraryLicences.put(floatingActionButtonSpeedDial, "Licensed under Apache-2.0");
libraryLicenceUrls.put(floatingActionButtonSpeedDial, "https://github.com/leinardi/FloatingActionButtonSpeedDial/blob/master/LICENSE");

String glide = "Glide";
libraryNames.add(glide);
libraryUrls.put(glide, "https://github.com/bumptech/glide");
libraryLicences.put(glide, "BSD, part MIT and Apache 2.0");
libraryLicenceUrls.put(glide, "https://github.com/bumptech/glide/blob/master/LICENSE");

String markDownView = "MarkDown View";
libraryNames.add(markDownView);
libraryUrls.put(markDownView, "https://github.com/falnatsheh/MarkdownView");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import ca.pkay.rcloneexplorer.FilePicker;
import ca.pkay.rcloneexplorer.Items.DirectoryObject;
import ca.pkay.rcloneexplorer.Items.FileItem;
import ca.pkay.rcloneexplorer.Items.RemoteItem;
import ca.pkay.rcloneexplorer.MainActivity;
import ca.pkay.rcloneexplorer.Dialogs.OpenAsDialog;
import ca.pkay.rcloneexplorer.R;
Expand Down Expand Up @@ -345,20 +346,26 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
for (File file : result) {
uploadList.add(file.getPath());
}
Intent intent = new Intent(getContext(), UploadService.class);
intent.putStringArrayListExtra(UploadService.LOCAL_PATH_ARG, uploadList);
intent.putExtra(UploadService.UPLOAD_PATH_ARG, directoryObject.getCurrentPath());
intent.putExtra(UploadService.REMOTE_ARG, remote);
context.startService(intent);

for (String uploadFile : uploadList) {
Intent intent = new Intent(getContext(), UploadService.class);
intent.putExtra(UploadService.LOCAL_PATH_ARG, uploadFile);
intent.putExtra(UploadService.UPLOAD_PATH_ARG, directoryObject.getCurrentPath());
intent.putExtra(UploadService.REMOTE_ARG, remote);
context.startService(intent);
}
} else if (requestCode == FILE_PICKER_DOWNLOAD_RESULT && resultCode == FragmentActivity.RESULT_OK) {
String selectedPath = data.getStringExtra(FilePicker.FILE_PICKER_RESULT);
final ArrayList<FileItem> downloadList = new ArrayList<>(recyclerViewAdapter.getSelectedItems());
recyclerViewAdapter.cancelSelection();
Intent intent = new Intent(getContext(), DownloadService.class);
intent.putParcelableArrayListExtra(DownloadService.DOWNLOAD_LIST_ARG, downloadList);
intent.putExtra(DownloadService.DOWNLOAD_PATH_ARG, selectedPath);
intent.putExtra(DownloadService.REMOTE_ARG, remote);
context.startService(intent);

for (FileItem downloadItem : downloadList) {
Intent intent = new Intent(getContext(), DownloadService.class);
intent.putExtra(DownloadService.DOWNLOAD_ITEM_ARG, downloadItem);
intent.putExtra(DownloadService.DOWNLOAD_PATH_ARG, selectedPath);
intent.putExtra(DownloadService.REMOTE_ARG, remote);
context.startService(intent);
}
} else if (requestCode == STREAMING_INTENT_RESULT) {
Intent serveIntent = new Intent(getContext(), StreamingService.class);
context.stopService(serveIntent);
Expand All @@ -372,6 +379,10 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menuPropertiesAction = menu.findItem(R.id.action_file_properties);
menuOpenAsAction = menu.findItem(R.id.action_open_as);
menuSelectAll = menu.findItem(R.id.action_select_all);

if (!RemoteItem.hasTrashCan(remoteType)) {
menu.findItem(R.id.action_empty_trash).setVisible(false);
}
}

@Override
Expand Down Expand Up @@ -401,6 +412,9 @@ public boolean onOptionsItemSelected(MenuItem item) {
case R.id.action_open_as:
showOpenAsDialog();
return true;
case R.id.action_empty_trash:
new EmptyTrashTash().execute();
return true;
default:
return super.onOptionsItemSelected(item);
}
Expand Down Expand Up @@ -640,13 +654,15 @@ private void moveLocationSelected() {
} else {
path2 = "//" + remote;
}
Intent intent = new Intent(context, BackgroundService.class);
intent.putExtra(BackgroundService.TASK_TYPE, BackgroundService.TASK_TYPE_MOVE);
intent.putExtra(BackgroundService.REMOTE_ARG, remote);
intent.putExtra(BackgroundService.MOVE_DEST_PATH, directoryObject.getCurrentPath());
intent.putExtra(BackgroundService.MOVE_LIST, moveList);
intent.putExtra(BackgroundService.PATH2, path2);
context.startService(intent);
for (FileItem moveItem : moveList) {
Intent intent = new Intent(context, BackgroundService.class);
intent.putExtra(BackgroundService.TASK_TYPE, BackgroundService.TASK_TYPE_MOVE);
intent.putExtra(BackgroundService.REMOTE_ARG, remote);
intent.putExtra(BackgroundService.MOVE_DEST_PATH, directoryObject.getCurrentPath());
intent.putExtra(BackgroundService.MOVE_ITEM, moveItem);
intent.putExtra(BackgroundService.PATH2, path2);
context.startService(intent);
}
Toasty.info(context, getString(R.string.moving_info), Toast.LENGTH_SHORT, true).show();
moveList.clear();
unlockOrientation();
Expand Down Expand Up @@ -1003,12 +1019,14 @@ private void deleteClicked() {
@Override
public void onClick(DialogInterface dialog, int which) {
recyclerViewAdapter.cancelSelection();
Intent intent = new Intent(context, BackgroundService.class);
intent.putExtra(BackgroundService.TASK_TYPE, BackgroundService.TASK_TYPE_DELETE);
intent.putExtra(BackgroundService.REMOTE_ARG, remote);
intent.putExtra(BackgroundService.DELETE_LIST, deleteList);
intent.putExtra(BackgroundService.PATH, directoryObject.getCurrentPath());
context.startService(intent);
for (FileItem deleteItem : deleteList) {
Intent intent = new Intent(context, BackgroundService.class);
intent.putExtra(BackgroundService.TASK_TYPE, BackgroundService.TASK_TYPE_DELETE);
intent.putExtra(BackgroundService.REMOTE_ARG, remote);
intent.putExtra(BackgroundService.DELETE_ITEM, deleteItem);
intent.putExtra(BackgroundService.PATH, directoryObject.getCurrentPath());
context.startService(intent);
}
Toasty.info(context, getString(R.string.deleting_info), Toast.LENGTH_SHORT, true).show();
}
});
Expand Down Expand Up @@ -1336,7 +1354,7 @@ protected Boolean doInBackground(FileItem... fileItems) {

fileLocation = saveLocation + "/" + fileItem.getName();

process = rclone.downloadItems(remote, fileItem, saveLocation);
process = rclone.downloadFile(remote, fileItem, saveLocation);

try {
process.waitFor();
Expand Down Expand Up @@ -1479,4 +1497,23 @@ protected Void doInBackground(FileItem... fileItems) {
return null;
}
}

@SuppressLint("StaticFieldLeak")
private class EmptyTrashTash extends AsyncTask<Void, Void, Boolean> {

@Override
protected Boolean doInBackground(Void... voids) {
return rclone.emptyTrashCan(remote);
}

@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
Toasty.success(context, getString(R.string.trash_emptied), Toast.LENGTH_SHORT, true).show();
} else {
Toasty.error(context, getString(R.string.error_emptying_trash), Toast.LENGTH_SHORT, true).show();
}
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/ca/pkay/rcloneexplorer/Items/RemoteItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ public RemoteItem(String name, String type) {
this.type = type;
}

public static boolean hasTrashCan(String remoteType) {
switch (remoteType) {
case "drive":
case "pcloud":
case "yandex":
return true;
default:
return false;
}
}

public String getName() {
return name;
}
Expand Down
133 changes: 56 additions & 77 deletions app/src/main/java/ca/pkay/rcloneexplorer/Rclone.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,33 +228,7 @@ public Process serveHttp(String remote, String servePath) {
}
}

public List<Process> downloadItems(String remote, List<FileItem> downloadList, String downloadPath) {
List<Process> runningProcesses = new ArrayList<>();
Process process;
String[] command;
String remoteFilePath;
String localFilePath;

for (FileItem item : downloadList) {
remoteFilePath = remote + ":" + item.getPath();
if (item.isDir()) {
localFilePath = downloadPath + "/" + item.getName();
} else {
localFilePath = downloadPath;
}
command = createCommand("copy", remoteFilePath, localFilePath);

try {
process = Runtime.getRuntime().exec(command);
runningProcesses.add(process);
} catch (IOException e) {
e.printStackTrace();
}
}
return runningProcesses;
}

public Process downloadItems(String remote, FileItem downloadItem, String downloadPath) {
public Process downloadFile(String remote, FileItem downloadItem, String downloadPath) {
String[] command;
String remoteFilePath;
String localFilePath;
Expand All @@ -275,58 +249,51 @@ public Process downloadItems(String remote, FileItem downloadItem, String downlo
}
}

public List<Process> uploadFiles(String remote, String uploadPath, ArrayList<String> uploadList) {
List<Process> runningProcesses = new ArrayList<>();
Process process;
public Process uploadFile(String remote, String uploadPath, String uploadFile) {
String path;
String[] command;

for (String localPath : uploadList) {
File file = new File(localPath);
if (file.isDirectory()) {
int index = localPath.lastIndexOf('/');
String dirName = localPath.substring(index + 1);
path = (uploadPath.compareTo("//" + remote) == 0) ? remote + ":" + dirName: remote + ":" + uploadPath + "/" + dirName;
} else {
path = (uploadPath.compareTo("//" + remote) == 0) ? remote + ":" : remote + ":" + uploadPath;
}
File file = new File(uploadFile);
if (file.isDirectory()) {
int index = uploadFile.lastIndexOf('/');
String dirName = uploadFile.substring(index + 1);
path = (uploadPath.compareTo("//" + remote) == 0) ? remote + ":" + dirName: remote + ":" + uploadPath + "/" + dirName;
} else {
path = (uploadPath.compareTo("//" + remote) == 0) ? remote + ":" : remote + ":" + uploadPath;
}

command = createCommand("copy", localPath, path);
command = createCommand("copy", uploadFile, path);

try {
process = Runtime.getRuntime().exec(command);
runningProcesses.add(process);
} catch (IOException e) {
e.printStackTrace();
}
try {
return Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
return null;
}

return runningProcesses;
}

public Boolean deleteItems(String remote, List<FileItem> deleteList) {
public Boolean deleteItems(String remote, FileItem deleteItem) {
String[] command;
String filePath;
Boolean result = true;

for (FileItem item : deleteList) {
filePath = remote + ":" + item.getPath();
if (item.isDir()) {
command = createCommand("purge", filePath);
} else {
command = createCommand("delete", filePath);
}
filePath = remote + ":" + deleteItem.getPath();
if (deleteItem.isDir()) {
command = createCommand("purge", filePath);
} else {
command = createCommand("delete", filePath);
}

try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
if (process.exitValue() != 0) {
result = false;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
if (process.exitValue() != 0) {
result = false;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
result = false;
}
return result;
}
Expand All @@ -348,27 +315,25 @@ public Boolean makeDirectory(String remote, String path) {
return true;
}

public Boolean moveTo(String remote, List<FileItem> moveList, String newLocation) {
public Boolean moveTo(String remote, FileItem moveItem, String newLocation) {
String[] command;
String oldFilePath;
String newFilePath;
Boolean result = true;

for (FileItem fileItem : moveList) {
oldFilePath = remote + ":" + fileItem.getPath();
newFilePath = (newLocation.compareTo("//" + remote) == 0) ? remote + ":" + fileItem.getName() : remote + ":" + newLocation + "/" + fileItem.getName();
command = createCommand("moveto", oldFilePath, newFilePath);
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
if (process.exitValue() != 0) {
logErrorOutput(process);
result = false;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
oldFilePath = remote + ":" + moveItem.getPath();
newFilePath = (newLocation.compareTo("//" + remote) == 0) ? remote + ":" + moveItem.getName() : remote + ":" + newLocation + "/" + moveItem.getName();
command = createCommand("moveto", oldFilePath, newFilePath);
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
if (process.exitValue() != 0) {
logErrorOutput(process);
result = false;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
result = false;
}
return result;
}
Expand All @@ -391,6 +356,20 @@ public Boolean moveTo(String remote, String oldFile, String newFile) {
return true;
}

public boolean emptyTrashCan(String remote) {
String[] command = createCommand("cleanup", remote + ":");
Process process = null;

try {
process = Runtime.getRuntime().exec(command);
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}

return process != null && process.exitValue() == 0;
}

public String calculateMD5(String remote, FileItem fileItem) {
String remoteAndPath = remote + ":" + fileItem.getName();
String[] command = createCommand("md5sum", remoteAndPath);
Expand Down
Loading

0 comments on commit 5eae41d

Please sign in to comment.