From 1b37a244b319ca938faffa08628de40ac9419550 Mon Sep 17 00:00:00 2001 From: TranceLove Date: Sun, 26 Jul 2020 23:14:24 +0800 Subject: [PATCH 01/10] Make SSH and SMB HybridFile.isDirectory() run in background thread Fixes #1942. - Added a single thread ExecutorService to AppConfig, allow code that needs to run in background. It was chosen over existing HandlerThread since ExecutorService provides Future as return value and allow blocking while HandlerThread doesn't - rename SshConnectionPool.expungeAllConnections() to SshConnectionPool.shutdown() - SshConnectionPool uses Bill Pugh singleton pattern (static holder class) - made SSH and SMB HybridFile.isDirectory() run in background thread using Callable --- .../filemanager/application/AppConfig.java | 32 +++++++++++++++---- .../filemanager/filesystem/HybridFile.java | 15 +++++---- .../filesystem/ssh/SshClientUtils.java | 27 +++++++++------- .../filesystem/ssh/SshConnectionPool.java | 14 ++++---- .../ui/activities/MainActivity.java | 4 +-- .../ssh/AbstractSftpServerTest.java | 2 +- 6 files changed, 60 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java index da79e231e..ca1b811e9 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -21,6 +21,10 @@ package com.amaze.filemanager.application; import java.lang.ref.WeakReference; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import com.amaze.filemanager.database.ExplorerDatabase; import com.amaze.filemanager.database.UtilitiesDatabase; @@ -56,9 +60,13 @@ public class AppConfig extends GlideApplication { private ImageLoader imageLoader; private UtilsHandler utilsHandler; - private static Handler applicationhandler = new Handler(); + private static Handler applicationHandler = new Handler(); + private HandlerThread backgroundHandlerThread; private static Handler backgroundHandler; + + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private WeakReference mainActivityContext; private static ScreenUtils screenUtils; @@ -77,7 +85,6 @@ public class AppConfig extends GlideApplication { super.onCreate(); AppCompatDelegate.setCompatVectorFromResourcesEnabled( true); // selector in srcCompat isn't supported without this - backgroundHandlerThread = new HandlerThread("app_background"); instance = this; CustomSshJConfig.init(); @@ -87,9 +94,7 @@ public class AppConfig extends GlideApplication { utilsProvider = new UtilitiesProvider(this); utilsHandler = new UtilsHandler(this, utilitiesDatabase); - // FIXME: in unit tests when AppConfig is rapidly created/destroyed this call will cause - // IllegalThreadStateException. - // Until this gets fixed only one test case can be run in a time. - Raymond, 24/4/2018 + backgroundHandlerThread = new HandlerThread("app_background"); backgroundHandlerThread.start(); backgroundHandler = new Handler(backgroundHandlerThread.getLooper()); @@ -102,6 +107,21 @@ public class AppConfig extends GlideApplication { public void onTerminate() { super.onTerminate(); backgroundHandlerThread.quit(); + executorService.shutdownNow(); + } + + /** + * Convenience method to execute code that needs to avoid main thread. Delegate to + * + * @see ExecutorService#submit(Callable) + * @see Future + * @param callable {@link Callable} that contains logic that cannot run on main thread, like + * networking, calculation + * @param Return value type + * @return {@link Future} from submitting the callable to the executor service + */ + public Future execute(Callable callable) { + return executorService.submit(callable); } /** @@ -221,7 +241,7 @@ public class AppConfig extends GlideApplication { * @param r Runnable to run */ public void runInApplicationThread(Runnable r) { - applicationhandler.post(r); + applicationHandler.post(r); } public static synchronized AppConfig getInstance() { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 9f9ca9018..5470e8fa1 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -29,6 +29,8 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.EnumSet; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import com.amaze.filemanager.adapters.data.LayoutElementParcelable; import com.amaze.filemanager.application.AppConfig; @@ -521,13 +523,14 @@ public class HybridFile { }); case SMB: try { - isDirectory = new SmbFile(path).isDirectory(); - } catch (SmbException e) { + isDirectory = + AppConfig.getInstance() + .execute((Callable) () -> new SmbFile(path).isDirectory()) + .get(); + } catch (InterruptedException | ExecutionException e) { isDirectory = false; - e.printStackTrace(); - } catch (MalformedURLException e) { - isDirectory = false; - e.printStackTrace(); + if (e.getCause() != null) e.getCause().printStackTrace(); + else e.printStackTrace(); } break; case FILE: diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java index 55291b489..101130551 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.util.List; +import java.util.concurrent.Callable; import com.amaze.filemanager.R; import com.amaze.filemanager.application.AppConfig; @@ -52,7 +53,8 @@ import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.sftp.SFTPClient; public abstract class SshClientUtils { - private static final String TAG = "SshClientUtils"; + + private static final String TAG = SshClientUtils.class.getSimpleName(); /** * Execute the given SshClientTemplate. @@ -65,19 +67,20 @@ public abstract class SshClientUtils { * @return Template execution results */ public static final T execute(@NonNull SshClientTemplate template) { - SSHClient client = null; + SSHClient client = SshConnectionPool.getInstance().getConnection(template.url); T retval = null; - try { - client = SshConnectionPool.getInstance().getConnection(template.url); - if (client != null) retval = template.execute(client); - else throw new RuntimeException("Unable to execute template"); - } catch (Exception e) { - Log.e(TAG, "Error executing template method", e); - } finally { - if (client != null && template.closeClientOnFinish) { - tryDisconnect(client); + if (client != null) { + try { + retval = + AppConfig.getInstance().execute((Callable) () -> template.execute(client)).get(); + } catch (Exception e) { + Log.e(TAG, "Error executing template method", e); + } finally { + if (template.closeClientOnFinish) { + tryDisconnect(client); + } } - } + } else throw new RuntimeException("Unable to execute template"); return retval; } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java index 2c598a163..eabbaf7da 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java @@ -58,12 +58,14 @@ public class SshConnectionPool { private static final String TAG = SshConnectionPool.class.getSimpleName(); - private static SshConnectionPool instance = null; - private final Map connections; private SshConnectionPool() { - connections = new ConcurrentHashMap(); + connections = new ConcurrentHashMap<>(); + } + + private static final class SshConnectionPoolHolder { + private static final SshConnectionPool instance = new SshConnectionPool(); } /** @@ -72,9 +74,7 @@ public class SshConnectionPool { * @return {@link SshConnectionPool} instance */ public static final SshConnectionPool getInstance() { - if (instance == null) instance = new SshConnectionPool(); - - return instance; + return SshConnectionPoolHolder.instance; } /** @@ -172,7 +172,7 @@ public class SshConnectionPool { * @see MainActivity#onDestroy() * @see MainActivity#exit() */ - public void expungeAllConnections() { + public void shutdown() { AppConfig.runInBackground( () -> { if (!connections.isEmpty()) { diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 2359410cc..63c250209 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -838,7 +838,7 @@ public class MainActivity extends PermissionsActivity public void exit() { if (backPressedToExitOnce) { - SshConnectionPool.getInstance().expungeAllConnections(); + SshConnectionPool.getInstance().shutdown(); finish(); if (isRootExplorer()) { // TODO close all shells @@ -1298,7 +1298,7 @@ public class MainActivity extends PermissionsActivity // TODO: 6/5/2017 Android may choose to not call this method before destruction // TODO: https://developer.android.com/reference/android/app/Activity.html#onDestroy%28%29 closeInteractiveShell(); - SshConnectionPool.getInstance().expungeAllConnections(); + SshConnectionPool.getInstance().shutdown(); } /** Closes the interactive shell and threads associated */ diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/ssh/AbstractSftpServerTest.java b/app/src/test/java/com/amaze/filemanager/filesystem/ssh/AbstractSftpServerTest.java index 06a62d0a8..944921e7b 100644 --- a/app/src/test/java/com/amaze/filemanager/filesystem/ssh/AbstractSftpServerTest.java +++ b/app/src/test/java/com/amaze/filemanager/filesystem/ssh/AbstractSftpServerTest.java @@ -75,7 +75,7 @@ public abstract class AbstractSftpServerTest { @After public void tearDown() throws IOException { - SshConnectionPool.getInstance().expungeAllConnections(); + SshConnectionPool.getInstance().shutdown(); if (server != null && server.isOpen()) { server.stop(true); } From c13710a7fed6de0a8f584b73b092bbebd5e620ac Mon Sep 17 00:00:00 2001 From: TranceLove Date: Mon, 27 Jul 2020 13:21:51 +0800 Subject: [PATCH 02/10] Remove background handler thread in favour of ExecutorService It was mainly used by UtilsHandler to save data to database while avoiding the main thread, so it should be fine. Also removed redundant throwing RuntimeException in SshClientUtils --- .../filemanager/application/AppConfig.java | 15 +- .../asynchronous/asynctasks/MoveFiles.java | 39 ++-- .../filemanager/database/CryptHandler.java | 8 +- .../filemanager/database/SortHandler.java | 6 +- .../filemanager/database/UtilsHandler.java | 168 +++++++++--------- .../filesystem/ssh/SshClientUtils.java | 2 +- .../filesystem/ssh/SshConnectionPool.java | 19 +- .../ui/activities/MainActivity.java | 46 ++--- .../ui/dialogs/SftpConnectDialog.java | 44 ++--- .../preference_fragments/FoldersPref.java | 15 +- .../amaze/filemanager/utils/DataUtils.java | 2 +- .../filemanager/test/ShadowCryptUtilTest.java | 12 +- .../com/amaze/filemanager/test/TestUtils.java | 13 +- 13 files changed, 201 insertions(+), 188 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java index ca1b811e9..6854ea612 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -42,7 +42,6 @@ import android.app.Application; import android.content.Context; import android.os.AsyncTask; import android.os.Handler; -import android.os.HandlerThread; import android.os.StrictMode; import android.widget.Toast; @@ -62,9 +61,6 @@ public class AppConfig extends GlideApplication { private static Handler applicationHandler = new Handler(); - private HandlerThread backgroundHandlerThread; - private static Handler backgroundHandler; - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private WeakReference mainActivityContext; @@ -94,10 +90,6 @@ public class AppConfig extends GlideApplication { utilsProvider = new UtilitiesProvider(this); utilsHandler = new UtilsHandler(this, utilitiesDatabase); - backgroundHandlerThread = new HandlerThread("app_background"); - backgroundHandlerThread.start(); - backgroundHandler = new Handler(backgroundHandlerThread.getLooper()); - // disabling file exposure method check for api n+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); @@ -106,7 +98,6 @@ public class AppConfig extends GlideApplication { @Override public void onTerminate() { super.onTerminate(); - backgroundHandlerThread.quit(); executorService.shutdownNow(); } @@ -129,10 +120,8 @@ public class AppConfig extends GlideApplication { * this runnable is executed, and {@link #runInBackground(Runnable)} in case we need to execute * something after execution in background */ - public static void runInBackground(Runnable runnable) { - synchronized (backgroundHandler) { - backgroundHandler.post(runnable); - } + public void runInBackground(Runnable runnable) { + executorService.submit(runnable); } /** diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFiles.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFiles.java index 1647b3987..0562692d9 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFiles.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFiles.java @@ -194,27 +194,28 @@ public class MoveFiles extends AsyncTask, String, Boolean> { } // updating encrypted db entry if any encrypted file was moved - AppConfig.runInBackground( - () -> { - for (int i = 0; i < paths.size(); i++) { - for (HybridFileParcelable file : files.get(i)) { - if (file.getName(context).endsWith(CryptUtil.CRYPT_EXTENSION)) { - try { - CryptHandler cryptHandler = CryptHandler.getInstance(); - EncryptedEntry oldEntry = cryptHandler.findEntry(file.getPath()); - EncryptedEntry newEntry = new EncryptedEntry(); - newEntry.setId(oldEntry.getId()); - newEntry.setPassword(oldEntry.getPassword()); - newEntry.setPath(paths.get(i) + "/" + file.getName(context)); - cryptHandler.updateEntry(oldEntry, newEntry); - } catch (Exception e) { - e.printStackTrace(); - // couldn't change the entry, leave it alone + AppConfig.getInstance() + .runInBackground( + () -> { + for (int i = 0; i < paths.size(); i++) { + for (HybridFileParcelable file : files.get(i)) { + if (file.getName(context).endsWith(CryptUtil.CRYPT_EXTENSION)) { + try { + CryptHandler cryptHandler = CryptHandler.getInstance(); + EncryptedEntry oldEntry = cryptHandler.findEntry(file.getPath()); + EncryptedEntry newEntry = new EncryptedEntry(); + newEntry.setId(oldEntry.getId()); + newEntry.setPassword(oldEntry.getPassword()); + newEntry.setPath(paths.get(i) + "/" + file.getName(context)); + cryptHandler.updateEntry(oldEntry, newEntry); + } catch (Exception e) { + e.printStackTrace(); + // couldn't change the entry, leave it alone + } + } } } - } - } - }); + }); } else { diff --git a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java index 060ce7007..5d742c2b4 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java @@ -44,15 +44,17 @@ public class CryptHandler { } public void addEntry(EncryptedEntry encryptedEntry) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().insert(encryptedEntry)); + AppConfig.getInstance() + .runInBackground(() -> database.encryptedEntryDao().insert(encryptedEntry)); } public void clear(String path) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().delete(path)); + AppConfig.getInstance().runInBackground(() -> database.encryptedEntryDao().delete(path)); } public void updateEntry(EncryptedEntry oldEncryptedEntry, EncryptedEntry newEncryptedEntry) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().update(newEncryptedEntry)); + AppConfig.getInstance() + .runInBackground(() -> database.encryptedEntryDao().update(newEncryptedEntry)); } public EncryptedEntry findEntry(String path) { diff --git a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java index 0d690a202..ef8077420 100644 --- a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java @@ -70,15 +70,15 @@ public class SortHandler { } public void addEntry(Sort sort) { - AppConfig.runInBackground(() -> database.sortDao().insert(sort)); + AppConfig.getInstance().runInBackground(() -> database.sortDao().insert(sort)); } public void clear(String path) { - AppConfig.runInBackground(() -> database.sortDao().clear(path)); + AppConfig.getInstance().runInBackground(() -> database.sortDao().clear(path)); } public void updateEntry(Sort oldSort, Sort newSort) { - AppConfig.runInBackground(() -> database.sortDao().update(newSort)); + AppConfig.getInstance().runInBackground(() -> database.sortDao().update(newSort)); } @Nullable diff --git a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java index b4785fd8d..776f5dadb 100644 --- a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java @@ -78,80 +78,83 @@ public class UtilsHandler { } public void saveToDatabase(OperationData operationData) { - AppConfig.runInBackground( - () -> { - switch (operationData.type) { - case HIDDEN: - utilitiesDatabase.hiddenEntryDao().insert(new Hidden(operationData.path)); - break; - case HISTORY: - utilitiesDatabase.historyEntryDao().insert(new History(operationData.path)); - break; - case LIST: - utilitiesDatabase - .listEntryDao() - .insert( - new com.amaze.filemanager.database.models.utilities.List(operationData.path)); - break; - case GRID: - utilitiesDatabase.gridEntryDao().insert(new Grid(operationData.path)); - break; - case BOOKMARKS: - utilitiesDatabase - .bookmarkEntryDao() - .insert(new Bookmark(operationData.name, operationData.path)); - break; - case SMB: - utilitiesDatabase - .smbEntryDao() - .insert(new SmbEntry(operationData.name, operationData.path)); - break; - case SFTP: - utilitiesDatabase - .sftpEntryDao() - .insert( - new SftpEntry( - operationData.path, - operationData.name, - operationData.hostKey, - operationData.sshKeyName, - operationData.sshKey)); - break; - default: - throw new IllegalStateException("Unidentified operation!"); - } - }); + AppConfig.getInstance() + .runInBackground( + () -> { + switch (operationData.type) { + case HIDDEN: + utilitiesDatabase.hiddenEntryDao().insert(new Hidden(operationData.path)); + break; + case HISTORY: + utilitiesDatabase.historyEntryDao().insert(new History(operationData.path)); + break; + case LIST: + utilitiesDatabase + .listEntryDao() + .insert( + new com.amaze.filemanager.database.models.utilities.List( + operationData.path)); + break; + case GRID: + utilitiesDatabase.gridEntryDao().insert(new Grid(operationData.path)); + break; + case BOOKMARKS: + utilitiesDatabase + .bookmarkEntryDao() + .insert(new Bookmark(operationData.name, operationData.path)); + break; + case SMB: + utilitiesDatabase + .smbEntryDao() + .insert(new SmbEntry(operationData.name, operationData.path)); + break; + case SFTP: + utilitiesDatabase + .sftpEntryDao() + .insert( + new SftpEntry( + operationData.path, + operationData.name, + operationData.hostKey, + operationData.sshKeyName, + operationData.sshKey)); + break; + default: + throw new IllegalStateException("Unidentified operation!"); + } + }); } public void removeFromDatabase(OperationData operationData) { - AppConfig.runInBackground( - () -> { - switch (operationData.type) { - case HIDDEN: - utilitiesDatabase.hiddenEntryDao().deleteByPath(operationData.path); - break; - case HISTORY: - utilitiesDatabase.historyEntryDao().deleteByPath(operationData.path); - break; - case LIST: - utilitiesDatabase.listEntryDao().deleteByPath(operationData.path); - break; - case GRID: - utilitiesDatabase.gridEntryDao().deleteByPath(operationData.path); - break; - case BOOKMARKS: - removeBookmarksPath(operationData.name, operationData.path); - break; - case SMB: - removeSmbPath(operationData.name, operationData.path); - break; - case SFTP: - removeSftpPath(operationData.name, operationData.path); - break; - default: - throw new IllegalStateException("Unidentified operation!"); - } - }); + AppConfig.getInstance() + .runInBackground( + () -> { + switch (operationData.type) { + case HIDDEN: + utilitiesDatabase.hiddenEntryDao().deleteByPath(operationData.path); + break; + case HISTORY: + utilitiesDatabase.historyEntryDao().deleteByPath(operationData.path); + break; + case LIST: + utilitiesDatabase.listEntryDao().deleteByPath(operationData.path); + break; + case GRID: + utilitiesDatabase.gridEntryDao().deleteByPath(operationData.path); + break; + case BOOKMARKS: + removeBookmarksPath(operationData.name, operationData.path); + break; + case SMB: + removeSmbPath(operationData.name, operationData.path); + break; + case SFTP: + removeSftpPath(operationData.name, operationData.path); + break; + default: + throw new IllegalStateException("Unidentified operation!"); + } + }); } public void addCommonBookmarks() { @@ -190,7 +193,7 @@ public class UtilsHandler { entry.sshKey = sshKey; } - AppConfig.runInBackground(() -> utilitiesDatabase.sftpEntryDao().update(entry)); + AppConfig.getInstance().runInBackground(() -> utilitiesDatabase.sftpEntryDao().update(entry)); } public LinkedList getHistoryLinkedList() { @@ -321,15 +324,16 @@ public class UtilsHandler { } public void clearTable(Operation table) { - AppConfig.runInBackground( - () -> { - switch (table) { - case HISTORY: - utilitiesDatabase.historyEntryDao().clear(); - break; - default: - break; - } - }); + AppConfig.getInstance() + .runInBackground( + () -> { + switch (table) { + case HISTORY: + utilitiesDatabase.historyEntryDao().clear(); + break; + default: + break; + } + }); } } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java index 101130551..40c2fa54a 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java @@ -80,7 +80,7 @@ public abstract class SshClientUtils { tryDisconnect(client); } } - } else throw new RuntimeException("Unable to execute template"); + } return retval; } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java index eabbaf7da..6fa2c63e3 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPool.java @@ -173,15 +173,16 @@ public class SshConnectionPool { * @see MainActivity#exit() */ public void shutdown() { - AppConfig.runInBackground( - () -> { - if (!connections.isEmpty()) { - for (SSHClient connection : connections.values()) { - SshClientUtils.tryDisconnect(connection); - } - connections.clear(); - } - }); + AppConfig.getInstance() + .runInBackground( + () -> { + if (!connections.isEmpty()) { + for (SSHClient connection : connections.values()) { + SshClientUtils.tryDisconnect(connection); + } + connections.clear(); + } + }); } // Logic for creating SSH connection. Depends on password existence in given Uri password or diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 63c250209..86b7b2ce8 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -1097,12 +1097,13 @@ public class MainActivity extends PermissionsActivity int pathLayout = dataUtils.getListOrGridForPath(ma.getCurrentPath(), DataUtils.LIST); if (ma.IS_LIST) { if (pathLayout == DataUtils.LIST) { - AppConfig.runInBackground( - () -> { - utilsHandler.removeFromDatabase( - new OperationData( - UtilsHandler.Operation.LIST, mainFragment.getCurrentPath())); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.removeFromDatabase( + new OperationData( + UtilsHandler.Operation.LIST, mainFragment.getCurrentPath())); + }); } utilsHandler.saveToDatabase( new OperationData(UtilsHandler.Operation.GRID, mainFragment.getCurrentPath())); @@ -1110,12 +1111,13 @@ public class MainActivity extends PermissionsActivity dataUtils.setPathAsGridOrList(ma.getCurrentPath(), DataUtils.GRID); } else { if (pathLayout == DataUtils.GRID) { - AppConfig.runInBackground( - () -> { - utilsHandler.removeFromDatabase( - new OperationData( - UtilsHandler.Operation.GRID, mainFragment.getCurrentPath())); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.removeFromDatabase( + new OperationData( + UtilsHandler.Operation.GRID, mainFragment.getCurrentPath())); + }); } utilsHandler.saveToDatabase( @@ -1813,10 +1815,11 @@ public class MainActivity extends PermissionsActivity if (i != -1) { dataUtils.removeServer(i); - AppConfig.runInBackground( - () -> { - utilsHandler.renameSMB(oldname, oldPath, name, path); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.renameSMB(oldname, oldPath, name, path); + }); // mainActivity.grid.removePath(oldname, oldPath, DataUtils.SMB); } dataUtils.addServer(s); @@ -1833,11 +1836,12 @@ public class MainActivity extends PermissionsActivity if (i != -1) { dataUtils.removeServer(i); - AppConfig.runInBackground( - () -> { - utilsHandler.removeFromDatabase( - new OperationData(UtilsHandler.Operation.SMB, name, path)); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.removeFromDatabase( + new OperationData(UtilsHandler.Operation.SMB, name, path)); + }); // grid.removePath(name, path, DataUtils.SMB); drawer.refreshDrawer(); } diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SftpConnectDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SftpConnectDialog.java index 026fc4141..746e990b0 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SftpConnectDialog.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SftpConnectDialog.java @@ -309,17 +309,18 @@ public class SftpConnectDialog extends DialogFragment { if (i != -1) { DataUtils.getInstance().removeServer(i); - AppConfig.runInBackground( - () -> { - utilsHandler.removeFromDatabase( - new OperationData( - UtilsHandler.Operation.SFTP, - path, - connectionName, - null, - null, - null)); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.removeFromDatabase( + new OperationData( + UtilsHandler.Operation.SFTP, + path, + connectionName, + null, + null, + null)); + }); ((MainActivity) getActivity()).getDrawer().refreshDrawer(); } dialog.dismiss(); @@ -476,16 +477,17 @@ public class SftpConnectDialog extends DialogFragment { Collections.sort(DataUtils.getInstance().getServers(), new BookSorter()); ((MainActivity) getActivity()).getDrawer().refreshDrawer(); - AppConfig.runInBackground( - () -> { - utilsHandler.updateSsh( - connectionName, - getArguments().getString("name"), - encryptedPath, - hostKeyFingerprint, - selectedParsedKeyPairName, - getPemContents()); - }); + AppConfig.getInstance() + .runInBackground( + () -> { + utilsHandler.updateSsh( + connectionName, + getArguments().getString("name"), + encryptedPath, + hostKeyFingerprint, + selectedParsedKeyPairName, + getPemContents()); + }); dismiss(); return true; diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.java index 40bd05778..2b600a7ee 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.java @@ -227,13 +227,14 @@ public class FoldersPref extends PreferenceFragment new String[] {editText1.getText().toString(), editText2.getText().toString()}; dataUtils.addBook(values); - AppConfig.runInBackground( - () -> - utilsHandler.renameBookmark( - oldName, - oldPath, - editText1.getText().toString(), - editText2.getText().toString())); + AppConfig.getInstance() + .runInBackground( + () -> + utilsHandler.renameBookmark( + oldName, + oldPath, + editText1.getText().toString(), + editText2.getText().toString())); dialog.dismiss(); }); diff --git a/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java b/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java index 33dc11d1b..b029775b5 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java +++ b/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java @@ -395,7 +395,7 @@ public class DataUtils { public void clearHistory() { history.clear(); if (dataChangeListener != null) { - AppConfig.runInBackground(() -> dataChangeListener.onHistoryCleared()); + AppConfig.getInstance().runInBackground(() -> dataChangeListener.onHistoryCleared()); } } diff --git a/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java b/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java index 9fcf0f6d3..af4df6fd3 100644 --- a/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java +++ b/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java @@ -20,10 +20,12 @@ package com.amaze.filemanager.test; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.security.GeneralSecurityException; +import java.util.concurrent.TimeUnit; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,7 +76,13 @@ public class ShadowCryptUtilTest { TestUtils.flushAppConfigHandlerThread(); - assertEquals(fingerprint, utilsHandler.getSshHostKey(url)); - utilitiesDatabase.close(); + await() + .atMost(10, TimeUnit.SECONDS) + .until( + () -> { + assertEquals(fingerprint, utilsHandler.getSshHostKey(url)); + utilitiesDatabase.close(); + return true; + }); } } diff --git a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java index 1cf11ec00..9adb2003b 100644 --- a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java +++ b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java @@ -20,9 +20,10 @@ package com.amaze.filemanager.test; -import static org.robolectric.Shadows.shadowOf; +import static org.awaitility.Awaitility.await; import java.lang.reflect.Field; +import java.util.concurrent.ExecutorService; import org.robolectric.util.Scheduler; @@ -49,16 +50,16 @@ public class TestUtils { * via reflection. * * @see Scheduler#advanceToNextPostedRunnable() - * @see AppConfig#backgroundHandler + * @see AppConfig#executorService * @see AppConfig#runInBackground(Runnable) */ public static void flushAppConfigHandlerThread() { try { - Field f = AppConfig.class.getDeclaredField("backgroundHandler"); + Field f = AppConfig.class.getDeclaredField("executorService"); f.setAccessible(true); - Handler h = (Handler) f.get(null); - Scheduler scheduler = shadowOf(h.getLooper()).getScheduler(); - scheduler.advanceToLastPostedRunnable(); + ExecutorService h = (ExecutorService) f.get(AppConfig.getInstance()); + h.shutdown(); + await().until(h::isTerminated); } catch (NoSuchFieldException | IllegalAccessException e) { throw new AssertionError("Unable to access backgroundHandler within AppConfig"); } From 1a6f8165f940f6980a3ecf71867853e59ca65d97 Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Mon, 31 Aug 2020 14:30:58 +0530 Subject: [PATCH 03/10] Merge release/3.5 --- app/build.gradle | 12 +- .../filemanager/application/AppConfig.java | 80 +--- .../filemanager/database/CloudHandler.java | 22 +- .../filemanager/database/CryptHandler.java | 17 +- .../filemanager/database/SortHandler.java | 9 +- .../filemanager/database/TabHandler.java | 13 +- .../filemanager/database/UtilsHandler.java | 201 +++++----- .../database/daos/BookmarkEntryDao.java | 13 +- .../database/daos/CloudEntryDao.java | 13 +- .../database/daos/EncryptedEntryDao.java | 13 +- .../database/daos/GridEntryDao.java | 10 +- .../database/daos/HiddenEntryDao.java | 10 +- .../database/daos/HistoryEntryDao.java | 12 +- .../database/daos/ListEntryDao.java | 10 +- .../database/daos/SftpEntryDao.java | 23 +- .../database/daos/SmbEntryDao.java | 15 +- .../filemanager/database/daos/SortDao.java | 10 +- .../filemanager/database/daos/TabDao.java | 11 +- .../filemanager/filesystem/FileUtil.java | 364 +++++++++--------- .../filemanager/filesystem/HybridFile.java | 39 +- .../filesystem/ssh/SshClientUtils.java | 5 +- .../ui/activities/MainActivity.java | 149 +++---- .../ui/dialogs/RenameBookmark.java | 1 - .../ui/dialogs/SmbConnectDialog.java | 1 + .../utils/annotations/BlockingAsync.java | 30 ++ .../filemanager/utils/aop/AsyncAspect.java | 46 +++ build.gradle | 10 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 28 files changed, 618 insertions(+), 523 deletions(-) create mode 100644 app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java create mode 100644 app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java diff --git a/app/build.gradle b/app/build.gradle index eac446cdb..be69ed9be 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' -apply plugin: 'jacoco-android' +apply plugin: "com.hiya.jacoco-android" +apply plugin: 'com.archinamon.aspectj' android { compileSdkVersion 28 @@ -114,6 +115,9 @@ dependencies { implementation "androidx.room:room-runtime:$roomVersion" implementation "com.android.billingclient:billing:$androidBillingVersion" annotationProcessor "androidx.room:room-compiler:$roomVersion" + // RxJava support for Room + implementation "androidx.room:room-rxjava2:$roomVersion" + implementation 'com.android.billingclient:billing:2.1.0' annotationProcessor 'androidx.annotation:annotation:1.1.0' //For tests @@ -218,6 +222,12 @@ dependencies { implementation 'org.tukaani:xz:1.8' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + // Because RxAndroid releases are few and far between, it is recommended you also + // explicitly depend on RxJava's latest version for bug fixes and new features. + // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version) + implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.1.1' + implementation project(':commons_compress_7z') } diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java index 665d82570..7b4ab4147 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import android.util.Log; import com.amaze.filemanager.database.ExplorerDatabase; import com.amaze.filemanager.database.UtilitiesDatabase; import com.amaze.filemanager.database.UtilsHandler; @@ -49,6 +50,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatDelegate; +import io.reactivex.Completable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import jcifs.Config; public class AppConfig extends GlideApplication { @@ -59,10 +64,6 @@ public class AppConfig extends GlideApplication { private ImageLoader imageLoader; private UtilsHandler utilsHandler; - private static Handler applicationHandler = new Handler(); - - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - private WeakReference mainActivityContext; private static ScreenUtils screenUtils; @@ -90,7 +91,7 @@ public class AppConfig extends GlideApplication { utilsProvider = new UtilitiesProvider(this); utilsHandler = new UtilsHandler(this, utilitiesDatabase); - runInBackground(jcifs.Config::registerSmbURLHandler); + runInBackground(Config::registerSmbURLHandler); // disabling file exposure method check for api n+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); @@ -100,22 +101,9 @@ public class AppConfig extends GlideApplication { @Override public void onTerminate() { super.onTerminate(); - executorService.shutdownNow(); } - /** - * Convenience method to execute code that needs to avoid main thread. Delegate to - * - * @see ExecutorService#submit(Callable) - * @see Future - * @param callable {@link Callable} that contains logic that cannot run on main thread, like - * networking, calculation - * @param Return value type - * @return {@link Future} from submitting the callable to the executor service - */ - public Future execute(Callable callable) { - return executorService.submit(callable); - } +// ExecutorService executorService = Executors.newCachedThreadPool(); /** * Post a runnable to handler. Use this in case we don't have any restriction to execute after @@ -123,51 +111,15 @@ public class AppConfig extends GlideApplication { * something after execution in background */ public void runInBackground(Runnable runnable) { - executorService.submit(runnable); - } + Completable.fromRunnable(runnable).subscribeOn(Schedulers.io()).subscribe(); + /*Single.just(runnable).subscribeOn(Schedulers.io()).subscribe(new Consumer() { + @Override + public void accept(Runnable runnable) throws Throwable { - /** - * A compact AsyncTask which runs which executes whatever is passed by callbacks. Supports any - * class that extends an object as param array, and result too. - */ - public static void runInParallel( - final CustomAsyncCallbacks customAsyncCallbacks) { - - synchronized (customAsyncCallbacks) { - new AsyncTask() { - @Override - protected void onPreExecute() { - super.onPreExecute(); - customAsyncCallbacks.onPreExecute(); - } - - @Override - protected Result doInBackground(Object... params) { - return customAsyncCallbacks.doInBackground(); - } - - @Override - protected void onPostExecute(Result aVoid) { - super.onPostExecute(aVoid); - customAsyncCallbacks.onPostExecute(aVoid); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, customAsyncCallbacks.parameters); - } - } - - /** Interface providing callbacks utilized by {@link #runInBackground(Runnable)} */ - public abstract static class CustomAsyncCallbacks { - public final @Nullable Params[] parameters; - - public CustomAsyncCallbacks(@Nullable Params[] params) { - parameters = params; - } - - public abstract Result doInBackground(); - - public void onPostExecute(Result result) {} - - public void onPreExecute() {} + } + });*/ +// executorService.execute(runnable); +// Observable.just(runnable).subscribeOn(AndroidSchedulers.mainThread()).subscribe(); } /** @@ -224,7 +176,7 @@ public class AppConfig extends GlideApplication { * @param r Runnable to run */ public void runInApplicationThread(Runnable r) { - applicationHandler.post(r); + Completable.fromRunnable(r).subscribeOn(AndroidSchedulers.mainThread()).cache(); } public static synchronized AppConfig getInstance() { diff --git a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java index 3c5d6e40e..164a2695f 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java @@ -31,6 +31,8 @@ import com.amaze.filemanager.utils.OpenMode; import android.content.Context; import androidx.annotation.NonNull; +import io.reactivex.functions.Consumer; +import io.reactivex.schedulers.Schedulers; /** Created by vishal on 18/4/17. */ public class CloudHandler { @@ -57,13 +59,18 @@ public class CloudHandler { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - database.cloudEntryDao().insert(cloudEntry); + database.cloudEntryDao().insert(cloudEntry).subscribeOn(Schedulers.io()).subscribe(); } public void clear(OpenMode serviceType) { - database - .cloudEntryDao() - .delete(database.cloudEntryDao().findByServiceType(serviceType.ordinal())); + database.cloudEntryDao().findByServiceType(serviceType.ordinal()).subscribeOn(Schedulers.io()).subscribe(new Consumer() { + @Override + public void accept(CloudEntry cloudEntry) { + database + .cloudEntryDao() + .delete(cloudEntry).subscribeOn(Schedulers.io()).subscribe(); + } + }); } public void updateEntry(OpenMode serviceType, CloudEntry newCloudEntry) @@ -71,20 +78,19 @@ public class CloudHandler { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - database.cloudEntryDao().update(newCloudEntry); + database.cloudEntryDao().update(newCloudEntry).subscribeOn(Schedulers.io()).subscribe(); } public CloudEntry findEntry(OpenMode serviceType) throws CloudPluginException { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - return database.cloudEntryDao().findByServiceType(serviceType.ordinal()); + return database.cloudEntryDao().findByServiceType(serviceType.ordinal()).subscribeOn(Schedulers.io()).blockingGet(); } public List getAllEntries() throws CloudPluginException { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - - return Arrays.asList(database.cloudEntryDao().list()); + return database.cloudEntryDao().list().subscribeOn(Schedulers.io()).toList().blockingGet(); } } diff --git a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java index 5d742c2b4..daafe40a9 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java @@ -24,6 +24,9 @@ import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.models.explorer.EncryptedEntry; import androidx.annotation.NonNull; +import io.reactivex.schedulers.Schedulers; + +import java.util.List; /** Created by vishal on 15/4/17. */ public class CryptHandler { @@ -44,24 +47,24 @@ public class CryptHandler { } public void addEntry(EncryptedEntry encryptedEntry) { - AppConfig.getInstance() - .runInBackground(() -> database.encryptedEntryDao().insert(encryptedEntry)); + database.encryptedEntryDao().insert(encryptedEntry).subscribeOn(Schedulers.io()).subscribe(); } public void clear(String path) { - AppConfig.getInstance().runInBackground(() -> database.encryptedEntryDao().delete(path)); + database.encryptedEntryDao().delete(path).subscribeOn(Schedulers.io()).subscribe(); } public void updateEntry(EncryptedEntry oldEncryptedEntry, EncryptedEntry newEncryptedEntry) { - AppConfig.getInstance() - .runInBackground(() -> database.encryptedEntryDao().update(newEncryptedEntry)); + database.encryptedEntryDao().update(newEncryptedEntry).subscribeOn(Schedulers.io()).subscribe(); } public EncryptedEntry findEntry(String path) { - return database.encryptedEntryDao().select(path); + return database.encryptedEntryDao().select(path).subscribeOn(Schedulers.io()).blockingGet(); } public EncryptedEntry[] getAllEntries() { - return database.encryptedEntryDao().list(); + List encryptedEntryList = database.encryptedEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet(); + EncryptedEntry[] encryptedEntries = new EncryptedEntry[encryptedEntryList.size()]; + return encryptedEntryList.toArray(encryptedEntries); } } diff --git a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java index ef8077420..e88b5dabb 100644 --- a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java @@ -34,6 +34,7 @@ import android.preference.PreferenceManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import io.reactivex.schedulers.Schedulers; /** Created by Ning on 5/28/2018. */ public class SortHandler { @@ -70,19 +71,19 @@ public class SortHandler { } public void addEntry(Sort sort) { - AppConfig.getInstance().runInBackground(() -> database.sortDao().insert(sort)); + database.sortDao().insert(sort).subscribeOn(Schedulers.io()).subscribe(); } public void clear(String path) { - AppConfig.getInstance().runInBackground(() -> database.sortDao().clear(path)); + database.sortDao().clear(path).subscribeOn(Schedulers.io()).subscribe(); } public void updateEntry(Sort oldSort, Sort newSort) { - AppConfig.getInstance().runInBackground(() -> database.sortDao().update(newSort)); + database.sortDao().update(newSort).subscribeOn(Schedulers.io()).subscribe(); } @Nullable public Sort findEntry(String path) { - return database.sortDao().find(path); + return database.sortDao().find(path).subscribeOn(Schedulers.io()).blockingGet(); } } diff --git a/app/src/main/java/com/amaze/filemanager/database/TabHandler.java b/app/src/main/java/com/amaze/filemanager/database/TabHandler.java index 93eef767f..8511e52f8 100644 --- a/app/src/main/java/com/amaze/filemanager/database/TabHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/TabHandler.java @@ -25,6 +25,9 @@ import com.amaze.filemanager.database.models.explorer.Tab; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import io.reactivex.schedulers.Schedulers; + +import java.util.List; /** Created by Vishal on 9/17/2014. */ public class TabHandler { @@ -45,19 +48,21 @@ public class TabHandler { } public void addTab(@NonNull Tab tab) { - database.tabDao().insertTab(tab); + database.tabDao().insertTab(tab).subscribeOn(Schedulers.io()).subscribe(); } public void clear() { - database.tabDao().clear(); + database.tabDao().clear().subscribeOn(Schedulers.io()).subscribe(); } @Nullable public Tab findTab(int tabNo) { - return database.tabDao().find(tabNo); + return database.tabDao().find(tabNo).subscribeOn(Schedulers.io()).blockingGet(); } public Tab[] getAllTabs() { - return database.tabDao().list(); + List tabList = database.tabDao().list().toList().subscribeOn(Schedulers.io()).blockingGet(); + Tab[] tabs = new Tab[tabList.size()]; + return tabList.toArray(tabs); } } diff --git a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java index 2db2c56f1..5052cb0c1 100644 --- a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java @@ -49,6 +49,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import io.reactivex.schedulers.Schedulers; /** * Created by Vishal on 29-05-2017. Class handles database with tables having list of various @@ -80,83 +81,75 @@ public class UtilsHandler { } public void saveToDatabase(OperationData operationData) { - AppConfig.getInstance() - .runInBackground( - () -> { - switch (operationData.type) { - case HIDDEN: - utilitiesDatabase.hiddenEntryDao().insert(new Hidden(operationData.path)); - break; - case HISTORY: - utilitiesDatabase.historyEntryDao().insert(new History(operationData.path)); - break; - case LIST: - utilitiesDatabase - .listEntryDao() - .insert( - new com.amaze.filemanager.database.models.utilities.List( - operationData.path)); - break; - case GRID: - utilitiesDatabase.gridEntryDao().insert(new Grid(operationData.path)); - break; - case BOOKMARKS: - utilitiesDatabase - .bookmarkEntryDao() - .insert(new Bookmark(operationData.name, operationData.path)); - break; - case SMB: - utilitiesDatabase - .smbEntryDao() - .insert(new SmbEntry(operationData.name, operationData.path)); - break; - case SFTP: - utilitiesDatabase - .sftpEntryDao() - .insert( - new SftpEntry( - operationData.path, - operationData.name, - operationData.hostKey, - operationData.sshKeyName, - operationData.sshKey)); - break; - default: - throw new IllegalStateException("Unidentified operation!"); - } - }); + switch (operationData.type) { + case HIDDEN: + utilitiesDatabase.hiddenEntryDao().insert(new Hidden(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case HISTORY: + utilitiesDatabase.historyEntryDao().insert(new History(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case LIST: + utilitiesDatabase + .listEntryDao() + .insert( + new com.amaze.filemanager.database.models.utilities.List( + operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case GRID: + utilitiesDatabase.gridEntryDao().insert(new Grid(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case BOOKMARKS: + utilitiesDatabase + .bookmarkEntryDao() + .insert(new Bookmark(operationData.name, operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case SMB: + utilitiesDatabase + .smbEntryDao() + .insert(new SmbEntry(operationData.name, operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + break; + case SFTP: + utilitiesDatabase + .sftpEntryDao() + .insert( + new SftpEntry( + operationData.path, + operationData.name, + operationData.hostKey, + operationData.sshKeyName, + operationData.sshKey)).subscribeOn(Schedulers.io()).subscribe(); + break; + default: + throw new IllegalStateException("Unidentified operation!"); + } } public void removeFromDatabase(OperationData operationData) { - AppConfig.getInstance() - .runInBackground( - () -> { - switch (operationData.type) { - case HIDDEN: - utilitiesDatabase.hiddenEntryDao().deleteByPath(operationData.path); - break; - case HISTORY: - utilitiesDatabase.historyEntryDao().deleteByPath(operationData.path); - break; - case LIST: - utilitiesDatabase.listEntryDao().deleteByPath(operationData.path); - break; - case GRID: - utilitiesDatabase.gridEntryDao().deleteByPath(operationData.path); - break; - case BOOKMARKS: - removeBookmarksPath(operationData.name, operationData.path); - break; - case SMB: - removeSmbPath(operationData.name, operationData.path); - break; - case SFTP: - removeSftpPath(operationData.name, operationData.path); - break; - default: - throw new IllegalStateException("Unidentified operation!"); - } - }); + switch (operationData.type) { + case HIDDEN: + utilitiesDatabase.hiddenEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + break; + case HISTORY: + utilitiesDatabase.historyEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + break; + case LIST: + utilitiesDatabase.listEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + break; + case GRID: + utilitiesDatabase.gridEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + break; + case BOOKMARKS: + removeBookmarksPath(operationData.name, operationData.path); + break; + case SMB: + removeSmbPath(operationData.name, operationData.path); + break; + case SFTP: + removeSftpPath(operationData.name, operationData.path); + break; + default: + throw new IllegalStateException("Unidentified operation!"); + } } public void addCommonBookmarks() { @@ -184,7 +177,7 @@ public class UtilsHandler { String sshKeyName, String sshKey) { - SftpEntry entry = utilitiesDatabase.sftpEntryDao().findByName(oldConnectionName); + SftpEntry entry = utilitiesDatabase.sftpEntryDao().findByName(oldConnectionName).subscribeOn(Schedulers.io()).blockingGet(); entry.name = connectionName; entry.path = path; @@ -195,12 +188,12 @@ public class UtilsHandler { entry.sshKey = sshKey; } - AppConfig.getInstance().runInBackground(() -> utilitiesDatabase.sftpEntryDao().update(entry)); + utilitiesDatabase.sftpEntryDao().update(entry).subscribeOn(Schedulers.io()).subscribe(); } public LinkedList getHistoryLinkedList() { LinkedList paths = new LinkedList<>(); - for (History history : utilitiesDatabase.historyEntryDao().list()) { + for (History history : utilitiesDatabase.historyEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { paths.add(history.path); } return paths; @@ -210,24 +203,24 @@ public class UtilsHandler { ConcurrentRadixTree paths = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory()); - for (String path : utilitiesDatabase.hiddenEntryDao().listPaths()) { + for (String path : utilitiesDatabase.hiddenEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()) { paths.put(path, VoidValue.SINGLETON); } return paths; } public ArrayList getListViewList() { - return new ArrayList<>(Arrays.asList(utilitiesDatabase.listEntryDao().listPaths())); + return new ArrayList<>(utilitiesDatabase.listEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getGridViewList() { - return new ArrayList<>(Arrays.asList(utilitiesDatabase.gridEntryDao().listPaths())); + return new ArrayList<>(utilitiesDatabase.gridEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getBookmarksList() { ArrayList row = new ArrayList<>(); - for (Bookmark bookmark : utilitiesDatabase.bookmarkEntryDao().list()) { + for (Bookmark bookmark : utilitiesDatabase.bookmarkEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { row.add(new String[] {bookmark.name, bookmark.path}); } return row; @@ -235,7 +228,7 @@ public class UtilsHandler { public ArrayList getSmbList() { ArrayList retval = new ArrayList(); - for (SmbEntry entry : utilitiesDatabase.smbEntryDao().list()) { + for (SmbEntry entry : utilitiesDatabase.smbEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { try { String path = SmbUtil.getSmbDecryptedPath(context, entry.path); @@ -256,7 +249,7 @@ public class UtilsHandler { public List getSftpList() { ArrayList retval = new ArrayList(); - for (SftpEntry entry : utilitiesDatabase.sftpEntryDao().list()) { + for (SftpEntry entry : utilitiesDatabase.sftpEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { String path = SshClientUtils.decryptSshPathAsNecessary(entry.path); if (path == null) { @@ -275,22 +268,22 @@ public class UtilsHandler { public String getSshHostKey(String uri) { uri = SshClientUtils.encryptSshPathAsNecessary(uri); if (uri != null) { - return utilitiesDatabase.sftpEntryDao().getSshHostKey(uri); + return utilitiesDatabase.sftpEntryDao().getSshHostKey(uri).subscribeOn(Schedulers.io()).blockingGet(); } else { return null; } } public String getSshAuthPrivateKeyName(String uri) { - return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKeyName(uri); + return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKeyName(uri).subscribeOn(Schedulers.io()).blockingGet(); } public String getSshAuthPrivateKey(String uri) { - return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKey(uri); + return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKey(uri).subscribeOn(Schedulers.io()).blockingGet(); } private void removeBookmarksPath(String name, String path) { - utilitiesDatabase.bookmarkEntryDao().deleteByNameAndPath(name, path); + utilitiesDatabase.bookmarkEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); } /** @@ -300,21 +293,21 @@ public class UtilsHandler { * must encrypt it's password fiend first first */ private void removeSmbPath(String name, String path) { - if ("".equals(path)) utilitiesDatabase.smbEntryDao().deleteByName(name); - else utilitiesDatabase.smbEntryDao().deleteByNameAndPath(name, path); + if ("".equals(path)) utilitiesDatabase.smbEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); + else utilitiesDatabase.smbEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); } private void removeSftpPath(String name, String path) { - if ("".equals(path)) utilitiesDatabase.sftpEntryDao().deleteByName(name); - else utilitiesDatabase.sftpEntryDao().deleteByNameAndPath(name, path); + if ("".equals(path)) utilitiesDatabase.sftpEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); + else utilitiesDatabase.sftpEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); } public void renameBookmark(String oldName, String oldPath, String newName, String newPath) { - Bookmark bookmark = utilitiesDatabase.bookmarkEntryDao().findByNameAndPath(oldName, oldPath); + Bookmark bookmark = utilitiesDatabase.bookmarkEntryDao().findByNameAndPath(oldName, oldPath).subscribeOn(Schedulers.io()).blockingGet(); bookmark.name = newName; bookmark.path = newPath; - utilitiesDatabase.bookmarkEntryDao().update(bookmark); + utilitiesDatabase.bookmarkEntryDao().update(bookmark).subscribeOn(Schedulers.io()).subscribe(); } public void renameSMB(String oldName, String oldPath, String newName, String newPath) { @@ -325,24 +318,20 @@ public class UtilsHandler { Log.e(TAG, "Error encrypting SMB path", e); } - SmbEntry smbEntry = utilitiesDatabase.smbEntryDao().findByNameAndPath(oldName, oldPath); + SmbEntry smbEntry = utilitiesDatabase.smbEntryDao().findByNameAndPath(oldName, oldPath).subscribeOn(Schedulers.io()).blockingGet(); smbEntry.name = newName; smbEntry.path = newPath; - utilitiesDatabase.smbEntryDao().update(smbEntry); + utilitiesDatabase.smbEntryDao().update(smbEntry).subscribeOn(Schedulers.io()).subscribe(); } public void clearTable(Operation table) { - AppConfig.getInstance() - .runInBackground( - () -> { - switch (table) { - case HISTORY: - utilitiesDatabase.historyEntryDao().clear(); - break; - default: - break; - } - }); + switch (table) { + case HISTORY: + utilitiesDatabase.historyEntryDao().clear().subscribeOn(Schedulers.io()).subscribe(); + break; + default: + break; + } } } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java index 7cdb17679..5987fb400 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java @@ -30,6 +30,9 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link Bookmark}. Concrete class is generated by Room during @@ -43,13 +46,13 @@ import androidx.room.Update; public interface BookmarkEntryDao { @Insert - public void insert(Bookmark instance); + Completable insert(Bookmark instance); @Update - public void update(Bookmark instance); + Completable update(Bookmark instance); @Query("SELECT * FROM " + TABLE_BOOKMARKS) - public Bookmark[] list(); + Observable list(); @Query( "SELECT * FROM " @@ -59,7 +62,7 @@ public interface BookmarkEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public Bookmark findByNameAndPath(String name, String path); + Single findByNameAndPath(String name, String path); @Query( "DELETE FROM " @@ -69,5 +72,5 @@ public interface BookmarkEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public void deleteByNameAndPath(String name, String path); + Completable deleteByNameAndPath(String name, String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java index 509f366c0..8a322ce72 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java @@ -30,6 +30,9 @@ import androidx.room.Delete; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link CloudEntry}. Concrete class is generated by Room @@ -43,18 +46,18 @@ import androidx.room.Update; public interface CloudEntryDao { @Insert - public void insert(CloudEntry entry); + Completable insert(CloudEntry entry); @Query( "SELECT * FROM " + TABLE_CLOUD_PERSIST + " WHERE " + COLUMN_CLOUD_SERVICE + " = :serviceType") - public CloudEntry findByServiceType(int serviceType); + Single findByServiceType(int serviceType); @Query("SELECT * FROM " + TABLE_CLOUD_PERSIST) - public CloudEntry[] list(); + Observable list(); @Update - public void update(CloudEntry entry); + Completable update(CloudEntry entry); @Delete - public void delete(CloudEntry entry); + Completable delete(CloudEntry entry); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java index 9c2e97849..875bfe6c4 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java @@ -30,6 +30,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link EncryptedEntry}. Concrete class is generated by Room @@ -43,18 +46,18 @@ import androidx.room.Update; public interface EncryptedEntryDao { @Insert - public void insert(EncryptedEntry entry); + Completable insert(EncryptedEntry entry); @Query("SELECT * FROM " + TABLE_ENCRYPTED + " WHERE " + COLUMN_PATH + " = :path") - public EncryptedEntry select(String path); + Single select(String path); @Update - public void update(EncryptedEntry entry); + Completable update(EncryptedEntry entry); @Transaction @Query("DELETE FROM " + TABLE_ENCRYPTED + " WHERE " + COLUMN_PATH + " = :path") - public void delete(String path); + Completable delete(String path); @Query("SELECT * FROM " + TABLE_ENCRYPTED) - public EncryptedEntry[] list(); + Observable list(); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java index 392b528fb..6701dc69b 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java @@ -29,6 +29,8 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; /** * {@link Dao} interface definition for {@link Grid}. Concrete class is generated by Room during @@ -42,14 +44,14 @@ import androidx.room.Update; public interface GridEntryDao { @Insert - public void insert(Grid instance); + Completable insert(Grid instance); @Update - public void update(Grid instance); + Completable update(Grid instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_GRID) - public String[] listPaths(); + Observable listPaths(); @Query("DELETE FROM " + TABLE_GRID + " WHERE " + COLUMN_PATH + " = :path") - public void deleteByPath(String path); + Completable deleteByPath(String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java index 783b5b2cc..382e1d028 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java @@ -29,6 +29,8 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; /** * {@link Dao} interface definition for {@link Hidden}. Concrete class is generated by Room during @@ -42,14 +44,14 @@ import androidx.room.Update; public interface HiddenEntryDao { @Insert - public void insert(Hidden instance); + Completable insert(Hidden instance); @Update - public void update(Hidden instance); + Completable update(Hidden instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_HIDDEN) - public String[] listPaths(); + Observable listPaths(); @Query("DELETE FROM " + TABLE_HIDDEN + " WHERE " + COLUMN_PATH + " = :path") - public void deleteByPath(String path); + Completable deleteByPath(String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java index 18106d657..c4e2554ec 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java @@ -30,6 +30,8 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; /** * {@link Dao} interface definition for {@link History}. Concrete class is generated by Room during @@ -43,17 +45,17 @@ import androidx.room.Update; public interface HistoryEntryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - public void insert(History instance); + Completable insert(History instance); @Update - public void update(History instance); + Completable update(History instance); @Query("SELECT * FROM " + TABLE_HISTORY) - public History[] list(); + Observable list(); @Query("DELETE FROM " + TABLE_HISTORY + " WHERE " + COLUMN_PATH + " = :path") - public void deleteByPath(String path); + Completable deleteByPath(String path); @Query("DELETE FROM " + TABLE_HISTORY) - public void clear(); + Completable clear(); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java index f5da3cc92..6ddd82305 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java @@ -30,6 +30,8 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; /** * {@link Dao} interface definition for {@link List}. Concrete class is generated by Room during @@ -43,14 +45,14 @@ import androidx.room.Update; public interface ListEntryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - public void insert(List instance); + Completable insert(List instance); @Update - public void update(List instance); + Completable update(List instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_LIST) - public String[] listPaths(); + Observable listPaths(); @Query("DELETE FROM " + TABLE_LIST + " WHERE " + COLUMN_PATH + " = :path") - public void deleteByPath(String path); + Completable deleteByPath(String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java index 30e8f6933..6c9eb49f3 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java @@ -33,6 +33,9 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link SftpEntry}. Concrete class is generated by Room @@ -46,13 +49,13 @@ import androidx.room.Update; public interface SftpEntryDao { @Insert - public void insert(SftpEntry instance); + Completable insert(SftpEntry instance); @Update - public void update(SftpEntry instance); + Completable update(SftpEntry instance); @Query("SELECT * FROM " + TABLE_SFTP) - public SftpEntry[] list(); + Observable list(); @Query( "SELECT * FROM " @@ -62,22 +65,22 @@ public interface SftpEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public SftpEntry findByNameAndPath(String name, String path); + Single findByNameAndPath(String name, String path); @Query("SELECT * FROM " + TABLE_SFTP + " WHERE " + COLUMN_NAME + " = :name") - public SftpEntry findByName(String name); + Single findByName(String name); @Query("SELECT " + COLUMN_HOST_PUBKEY + " FROM " + TABLE_SFTP + " WHERE PATH = :uri") - public String getSshHostKey(String uri); + Single getSshHostKey(String uri); @Query("SELECT " + COLUMN_PRIVATE_KEY_NAME + " FROM " + TABLE_SFTP + " WHERE PATH = :uri") - public String getSshAuthPrivateKeyName(String uri); + Single getSshAuthPrivateKeyName(String uri); @Query("SELECT " + COLUMN_PRIVATE_KEY + " FROM " + TABLE_SFTP + " WHERE PATH = :uri") - public String getSshAuthPrivateKey(String uri); + Single getSshAuthPrivateKey(String uri); @Query("DELETE FROM " + TABLE_SFTP + " WHERE " + COLUMN_NAME + " = :name") - public void deleteByName(String name); + Completable deleteByName(String name); @Query( "DELETE FROM " @@ -87,5 +90,5 @@ public interface SftpEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public void deleteByNameAndPath(String name, String path); + Completable deleteByNameAndPath(String name, String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java index e0cbcb640..8fc79a802 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java @@ -30,6 +30,9 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link SmbEntry}. Concrete class is generated by Room during @@ -43,13 +46,13 @@ import androidx.room.Update; public interface SmbEntryDao { @Insert - public void insert(SmbEntry instance); + Completable insert(SmbEntry instance); @Update - public void update(SmbEntry instance); + Completable update(SmbEntry instance); @Query("SELECT * FROM " + TABLE_SMB) - public SmbEntry[] list(); + Observable list(); @Query( "SELECT * FROM " @@ -59,10 +62,10 @@ public interface SmbEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public SmbEntry findByNameAndPath(String name, String path); + Single findByNameAndPath(String name, String path); @Query("DELETE FROM " + TABLE_SMB + " WHERE " + COLUMN_NAME + " = :name") - public void deleteByName(String name); + Completable deleteByName(String name); @Query( "DELETE FROM " @@ -72,5 +75,5 @@ public interface SmbEntryDao { + " = :name AND " + COLUMN_PATH + " = :path") - public void deleteByNameAndPath(String name, String path); + Completable deleteByNameAndPath(String name, String path); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java index 34f1c8598..749c895e7 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java @@ -30,6 +30,8 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link Sort}. Concrete class is generated by Room during @@ -43,15 +45,15 @@ import androidx.room.Update; public interface SortDao { @Insert - public void insert(Sort entity); + Completable insert(Sort entity); @Query("SELECT * FROM " + TABLE_SORT + " WHERE " + COLUMN_PATH + " = :path") - public Sort find(String path); + Single find(String path); @Transaction @Query("DELETE FROM " + TABLE_SORT + " WHERE " + COLUMN_PATH + " = :path") - public void clear(String path); + Completable clear(String path); @Update - public void update(Sort entity); + Completable update(Sort entity); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java index 096c22ac7..9714407e4 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java @@ -30,6 +30,9 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Transaction; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link Tab}. Concrete class is generated by Room during @@ -42,15 +45,15 @@ import androidx.room.Transaction; @Dao public interface TabDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - public void insertTab(Tab tab); + Completable insertTab(Tab tab); @Transaction @Query("DELETE FROM " + TABLE_TAB) - public void clear(); + Completable clear(); @Query("SELECT * FROM " + TABLE_TAB + " WHERE " + COLUMN_TAB_NO + " = :tabNo") - public Tab find(int tabNo); + Single find(int tabNo); @Query("SELECT * FROM " + TABLE_TAB) - public Tab[] list(); + Observable list(); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index 0bd3be912..575ea1865 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -71,6 +71,10 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.documentfile.provider.DocumentFile; +import io.reactivex.Maybe; +import io.reactivex.MaybeOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; import jcifs.smb.SmbException; import jcifs.smb.SmbFile; @@ -200,197 +204,201 @@ public abstract class FileUtil { @NonNull final ContentResolver contentResolver, @NonNull final String currentPath) { - AppConfig.runInParallel( - new AppConfig.CustomAsyncCallbacks>(null) { + Maybe.create( + (MaybeOnSubscribe>) + emitter -> { + List retval = new ArrayList<>(); - @Override - public List doInBackground() { + for (Uri uri : uris) { - List retval = new ArrayList<>(); + BufferedInputStream bufferedInputStream = null; + try { + bufferedInputStream = + new BufferedInputStream(contentResolver.openInputStream(uri)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return; + } - for (Uri uri : uris) { + BufferedOutputStream bufferedOutputStream = null; - BufferedInputStream bufferedInputStream = null; - try { - bufferedInputStream = new BufferedInputStream(contentResolver.openInputStream(uri)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } + try { + DocumentFile documentFile = DocumentFile.fromSingleUri(mainActivity, uri); + String filename = documentFile.getName(); + if (filename == null) { + filename = uri.getLastPathSegment(); - BufferedOutputStream bufferedOutputStream = null; + // For cleaning up slashes. Back in #1217 there is a case of + // Uri.getLastPathSegment() end up with a full file path + if (filename.contains("/")) + filename = filename.substring(filename.lastIndexOf('/') + 1); + } - try { - DocumentFile documentFile = DocumentFile.fromSingleUri(mainActivity, uri); - String filename = documentFile.getName(); - if (filename == null) { - filename = uri.getLastPathSegment(); + String finalFilePath = currentPath + "/" + filename; + DataUtils dataUtils = DataUtils.getInstance(); - // For cleaning up slashes. Back in #1217 there is a case of - // Uri.getLastPathSegment() end up with a full file path - if (filename.contains("/")) - filename = filename.substring(filename.lastIndexOf('/') + 1); - } + HybridFile hFile = new HybridFile(OpenMode.UNKNOWN, currentPath); + hFile.generateMode(mainActivity); - String finalFilePath = currentPath + "/" + filename; - DataUtils dataUtils = DataUtils.getInstance(); + switch (hFile.getMode()) { + case FILE: + case ROOT: + File targetFile = new File(finalFilePath); + if (!FileUtil.isWritableNormalOrSaf( + targetFile.getParentFile(), mainActivity.getApplicationContext())) { + AppConfig.toast( + mainActivity, + mainActivity.getResources().getString(R.string.not_allowed)); + return; + } - HybridFile hFile = new HybridFile(OpenMode.UNKNOWN, currentPath); - hFile.generateMode(mainActivity); + DocumentFile targetDocumentFile = + getDocumentFile( + targetFile, false, mainActivity.getApplicationContext()); - switch (hFile.getMode()) { - case FILE: - case ROOT: - File targetFile = new File(finalFilePath); - if (!FileUtil.isWritableNormalOrSaf( - targetFile.getParentFile(), mainActivity.getApplicationContext())) { - AppConfig.toast( + // Fallback, in case getDocumentFile() didn't properly return a + // DocumentFile + // instance + if (targetDocumentFile == null) + targetDocumentFile = DocumentFile.fromFile(targetFile); + + // Lazy check... and in fact, different apps may pass in URI in different + // formats, so we could only check filename matches + // FIXME?: Prompt overwrite instead of simply blocking + if (targetDocumentFile.exists() && targetDocumentFile.length() > 0) { + AppConfig.toast( + mainActivity, mainActivity.getString(R.string.cannot_overwrite)); + return; + } + + bufferedOutputStream = + new BufferedOutputStream( + contentResolver.openOutputStream(targetDocumentFile.getUri())); + retval.add(targetFile.getPath()); + break; + case SMB: + SmbFile targetSmbFile = new SmbFile(finalFilePath); + if (targetSmbFile.exists()) { + AppConfig.toast( + mainActivity, mainActivity.getString(R.string.cannot_overwrite)); + emitter.onError(new Exception()); + } else { + OutputStream outputStream = targetSmbFile.getOutputStream(); + bufferedOutputStream = new BufferedOutputStream(outputStream); + retval.add( + mainActivity.mainActivityHelper.parseSmbPath( + targetSmbFile.getPath())); + } + break; + case SFTP: + // FIXME: implement support + AppConfig.toast( + mainActivity, mainActivity.getString(R.string.not_allowed)); + emitter.onError(new Exception()); + case DROPBOX: + CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + String path = CloudUtil.stripPath(OpenMode.DROPBOX, finalFilePath); + cloudStorageDropbox.upload( + path, bufferedInputStream, documentFile.length(), true); + retval.add(path); + break; + case BOX: + CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + path = CloudUtil.stripPath(OpenMode.BOX, finalFilePath); + cloudStorageBox.upload( + path, bufferedInputStream, documentFile.length(), true); + retval.add(path); + break; + case ONEDRIVE: + CloudStorage cloudStorageOneDrive = + dataUtils.getAccount(OpenMode.ONEDRIVE); + path = CloudUtil.stripPath(OpenMode.ONEDRIVE, finalFilePath); + cloudStorageOneDrive.upload( + path, bufferedInputStream, documentFile.length(), true); + retval.add(path); + break; + case GDRIVE: + CloudStorage cloudStorageGDrive = dataUtils.getAccount(OpenMode.GDRIVE); + path = CloudUtil.stripPath(OpenMode.GDRIVE, finalFilePath); + cloudStorageGDrive.upload( + path, bufferedInputStream, documentFile.length(), true); + retval.add(path); + break; + case OTG: + DocumentFile documentTargetFile = + OTGUtil.getDocumentFile(finalFilePath, mainActivity, true); + + if (documentTargetFile.exists()) { + AppConfig.toast( + mainActivity, mainActivity.getString(R.string.cannot_overwrite)); + return; + } + + bufferedOutputStream = + new BufferedOutputStream( + contentResolver.openOutputStream(documentTargetFile.getUri()), + GenericCopyUtil.DEFAULT_BUFFER_SIZE); + + retval.add(documentTargetFile.getUri().getPath()); + break; + default: + return; + } + + int count = 0; + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + + while (count != -1) { + count = bufferedInputStream.read(buffer); + if (count != -1) { + + bufferedOutputStream.write(buffer, 0, count); + } + } + bufferedOutputStream.flush(); + + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + try { + if (bufferedInputStream != null) { + bufferedInputStream.close(); + } + if (bufferedOutputStream != null) { + bufferedOutputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + } + if (retval.size() > 0) { + emitter.onSuccess(retval); + } else { + emitter.onError(new Exception()); + } + }) + .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe( + paths -> { + if (paths != null) { + if (paths.size() == 1) { + Toast.makeText( mainActivity, - mainActivity.getResources().getString(R.string.not_allowed)); - return null; - } - - DocumentFile targetDocumentFile = - getDocumentFile(targetFile, false, mainActivity.getApplicationContext()); - - // Fallback, in case getDocumentFile() didn't properly return a DocumentFile - // instance - if (targetDocumentFile == null) - targetDocumentFile = DocumentFile.fromFile(targetFile); - - // Lazy check... and in fact, different apps may pass in URI in different - // formats, so we could only check filename matches - // FIXME?: Prompt overwrite instead of simply blocking - if (targetDocumentFile.exists() && targetDocumentFile.length() > 0) { - AppConfig.toast( - mainActivity, mainActivity.getString(R.string.cannot_overwrite)); - return null; - } - - bufferedOutputStream = - new BufferedOutputStream( - contentResolver.openOutputStream(targetDocumentFile.getUri())); - retval.add(targetFile.getPath()); - break; - case SMB: - SmbFile targetSmbFile = new SmbFile(finalFilePath); - if (targetSmbFile.exists()) { - AppConfig.toast( - mainActivity, mainActivity.getString(R.string.cannot_overwrite)); - return null; - } else { - OutputStream outputStream = targetSmbFile.getOutputStream(); - bufferedOutputStream = new BufferedOutputStream(outputStream); - retval.add(HybridFile.parseSmbPath(targetSmbFile.getPath())); - } - break; - case SFTP: - // FIXME: implement support - AppConfig.toast(mainActivity, mainActivity.getString(R.string.not_allowed)); - return null; - case DROPBOX: - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); - String path = CloudUtil.stripPath(OpenMode.DROPBOX, finalFilePath); - cloudStorageDropbox.upload( - path, bufferedInputStream, documentFile.length(), true); - retval.add(path); - break; - case BOX: - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); - path = CloudUtil.stripPath(OpenMode.BOX, finalFilePath); - cloudStorageBox.upload(path, bufferedInputStream, documentFile.length(), true); - retval.add(path); - break; - case ONEDRIVE: - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); - path = CloudUtil.stripPath(OpenMode.ONEDRIVE, finalFilePath); - cloudStorageOneDrive.upload( - path, bufferedInputStream, documentFile.length(), true); - retval.add(path); - break; - case GDRIVE: - CloudStorage cloudStorageGDrive = dataUtils.getAccount(OpenMode.GDRIVE); - path = CloudUtil.stripPath(OpenMode.GDRIVE, finalFilePath); - cloudStorageGDrive.upload( - path, bufferedInputStream, documentFile.length(), true); - retval.add(path); - break; - case OTG: - DocumentFile documentTargetFile = - OTGUtil.getDocumentFile(finalFilePath, mainActivity, true); - - if (documentTargetFile.exists()) { - AppConfig.toast( - mainActivity, mainActivity.getString(R.string.cannot_overwrite)); - return null; - } - - bufferedOutputStream = - new BufferedOutputStream( - contentResolver.openOutputStream(documentTargetFile.getUri()), - GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - retval.add(documentTargetFile.getUri().getPath()); - break; - default: - return null; - } - - int count = 0; - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - - while (count != -1) { - count = bufferedInputStream.read(buffer); - if (count != -1) { - - bufferedOutputStream.write(buffer, 0, count); - } - } - bufferedOutputStream.flush(); - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - - try { - - if (bufferedInputStream != null) { - bufferedInputStream.close(); - } - if (bufferedOutputStream != null) { - bufferedOutputStream.close(); - } - } catch (IOException e) { - e.printStackTrace(); + mainActivity.getString(R.string.saved_single_file, paths.get(0)), + Toast.LENGTH_LONG) + .show(); + } else { + Toast.makeText( + mainActivity, + mainActivity.getString(R.string.saved_multi_files, paths.size()), + Toast.LENGTH_LONG) + .show(); } } - } - return (retval.size() > 0) ? retval : null; - } - - @Override - public void onPostExecute(List result) { - if (result != null) { - List paths = (List) result; - if (paths.size() == 1) { - Toast.makeText( - mainActivity, - mainActivity.getString(R.string.saved_single_file, paths.get(0)), - Toast.LENGTH_LONG) - .show(); - } else { - Toast.makeText( - mainActivity, - mainActivity.getString(R.string.saved_multi_files, paths.size()), - Toast.LENGTH_LONG) - .show(); - } - } - } - }); + }); } /** diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 190b3e42f..3e3237ac0 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -20,18 +20,13 @@ package com.amaze.filemanager.filesystem; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; - +import android.content.ContentResolver; +import android.content.Context; +import android.os.Build; +import android.preference.PreferenceManager; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.documentfile.provider.DocumentFile; import com.amaze.filemanager.adapters.data.LayoutElementParcelable; import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.CloudHandler; @@ -49,6 +44,7 @@ import com.amaze.filemanager.utils.OTGUtil; import com.amaze.filemanager.utils.OnFileFound; import com.amaze.filemanager.utils.OpenMode; import com.amaze.filemanager.utils.RootUtils; +import com.amaze.filemanager.utils.annotations.*; import com.cloudrail.si.interfaces.CloudStorage; import com.cloudrail.si.types.SpaceAllocation; @@ -72,6 +68,16 @@ import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.sftp.SFTPClient; import net.schmizz.sshj.sftp.SFTPException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.EnumSet; + /** Created by Arpit on 07-07-2015. */ // Hybrid file for handeling all types of files public class HybridFile { @@ -439,8 +445,8 @@ public class HybridFile { return isDirectory; } + @BlockingAsync public boolean isDirectory(Context context) { - boolean isDirectory; switch (mode) { case SFTP: @@ -460,11 +466,8 @@ public class HybridFile { }); case SMB: try { - isDirectory = - AppConfig.getInstance() - .execute((Callable) () -> new SmbFile(path).isDirectory()) - .get(); - } catch (InterruptedException | ExecutionException e) { + isDirectory = new SmbFile(path).isDirectory(); + } catch (MalformedURLException | SmbException e) { isDirectory = false; if (e.getCause() != null) e.getCause().printStackTrace(); else e.printStackTrace(); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java index f4333f766..8962883ba 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java @@ -48,6 +48,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import io.reactivex.Single; +import io.reactivex.schedulers.Schedulers; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.sftp.FileAttributes; @@ -74,8 +76,7 @@ public abstract class SshClientUtils { T retval = null; if (client != null) { try { - retval = - AppConfig.getInstance().execute((Callable) () -> template.execute(client)).get(); + retval = (T) Single.just(template.execute(client)).subscribeOn(Schedulers.io()).blockingGet(); } catch (Exception e) { Log.e(TAG, "Error executing template method", e); } finally { diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index d16d79713..4af661764 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -153,6 +153,10 @@ import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; import eu.chainfire.libsuperuser.Shell; +import io.reactivex.Completable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Action; +import io.reactivex.schedulers.Schedulers; public class MainActivity extends PermissionsActivity implements SmbConnectionListener, @@ -368,79 +372,82 @@ public class MainActivity extends PermissionsActivity checkForExternalPermission(); - AppConfig.runInParallel( - new AppConfig.CustomAsyncCallbacks(null) { - @Override - public Void doInBackground() { - - dataUtils.setHiddenFiles(utilsHandler.getHiddenFilesConcurrentRadixTree()); - dataUtils.setHistory(utilsHandler.getHistoryLinkedList()); - dataUtils.setGridfiles(utilsHandler.getGridViewList()); - dataUtils.setListfiles(utilsHandler.getListViewList()); - dataUtils.setBooks(utilsHandler.getBookmarksList()); - ArrayList servers = new ArrayList(); - servers.addAll(utilsHandler.getSmbList()); - servers.addAll(utilsHandler.getSftpList()); - dataUtils.setServers(servers); - - return null; - } - - @Override - public void onPostExecute(Void result) { - - drawer.refreshDrawer(); - - if (savedInstanceState == null) { - if (openProcesses) { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace( - R.id.content_frame, new ProcessViewerFragment(), KEY_INTENT_PROCESS_VIEWER); - // transaction.addToBackStack(null); - drawer.setSomethingSelected(true); - openProcesses = false; - // title.setText(utils.getString(con, R.string.process_viewer)); - // Commit the transaction - transaction.commit(); - supportInvalidateOptionsMenu(); - } else if (intent.getAction() != null - && intent.getAction().equals(TileService.ACTION_QS_TILE_PREFERENCES)) { - // tile preferences, open ftp fragment - - FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction(); - transaction2.replace(R.id.content_frame, new FtpServerFragment()); - appBarLayout - .animate() - .translationY(0) - .setInterpolator(new DecelerateInterpolator(2)) - .start(); - - drawer.setSomethingSelected(true); - drawer.deselectEverything(); - transaction2.commit(); - } else { - if (path != null && path.length() > 0) { - HybridFile file = new HybridFile(OpenMode.UNKNOWN, path); - file.generateMode(MainActivity.this); - if (file.isDirectory(MainActivity.this)) goToMain(path); - else { - goToMain(null); - FileUtils.openFile(new File(path), MainActivity.this, getPrefs()); - } - } else { - goToMain(null); - } + Completable.fromRunnable( + new Runnable() { + @Override + public void run() { + dataUtils.setHiddenFiles(utilsHandler.getHiddenFilesConcurrentRadixTree()); + dataUtils.setHistory(utilsHandler.getHistoryLinkedList()); + dataUtils.setGridfiles(utilsHandler.getGridViewList()); + dataUtils.setListfiles(utilsHandler.getListViewList()); + dataUtils.setBooks(utilsHandler.getBookmarksList()); + ArrayList servers = new ArrayList(); + servers.addAll(utilsHandler.getSmbList()); + servers.addAll(utilsHandler.getSftpList()); + dataUtils.setServers(servers); } - } else { - pasteHelper = savedInstanceState.getParcelable(PASTEHELPER_BUNDLE); - oppathe = savedInstanceState.getString(KEY_OPERATION_PATH); - oppathe1 = savedInstanceState.getString(KEY_OPERATED_ON_PATH); - oparrayList = savedInstanceState.getParcelableArrayList(KEY_OPERATIONS_PATH_LIST); - operation = savedInstanceState.getInt(KEY_OPERATION); - // mainFragment = (Main) savedInstanceState.getParcelable("main_fragment"); - } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + new Action() { + @Override + public void run() { + drawer.refreshDrawer(); + invalidateFragmentAndBundle(savedInstanceState); + } + }); + } + + private void invalidateFragmentAndBundle(Bundle savedInstanceState) { + if (savedInstanceState == null) { + if (openProcesses) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace( + R.id.content_frame, new ProcessViewerFragment(), KEY_INTENT_PROCESS_VIEWER); + // transaction.addToBackStack(null); + drawer.setSomethingSelected(true); + openProcesses = false; + // title.setText(utils.getString(con, R.string.process_viewer)); + // Commit the transaction + transaction.commit(); + supportInvalidateOptionsMenu(); + } else if (intent.getAction() != null + && intent.getAction().equals(TileService.ACTION_QS_TILE_PREFERENCES)) { + // tile preferences, open ftp fragment + + FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction(); + transaction2.replace(R.id.content_frame, new FtpServerFragment()); + appBarLayout + .animate() + .translationY(0) + .setInterpolator(new DecelerateInterpolator(2)) + .start(); + + drawer.setSomethingSelected(true); + drawer.deselectEverything(); + transaction2.commit(); + } else { + if (path != null && path.length() > 0) { + HybridFile file = new HybridFile(OpenMode.UNKNOWN, path); + file.generateMode(MainActivity.this); + if (file.isDirectory(MainActivity.this)) goToMain(path); + else { + goToMain(null); + FileUtils.openFile(new File(path), MainActivity.this, getPrefs()); } - }); + } else { + goToMain(null); + } + } + } else { + pasteHelper = savedInstanceState.getParcelable(PASTEHELPER_BUNDLE); + oppathe = savedInstanceState.getString(KEY_OPERATION_PATH); + oppathe1 = savedInstanceState.getString(KEY_OPERATED_ON_PATH); + oparrayList = savedInstanceState.getParcelableArrayList(KEY_OPERATIONS_PATH_LIST); + operation = savedInstanceState.getInt(KEY_OPERATION); + // mainFragment = (Main) savedInstanceState.getParcelable("main_fragment"); + } } private void checkForExternalPermission() { diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/RenameBookmark.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/RenameBookmark.java index 5dd4631c6..d0b8e98eb 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/RenameBookmark.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/RenameBookmark.java @@ -114,7 +114,6 @@ public class RenameBookmark extends DialogFragment { if (studiomode != 0) { if (path.startsWith("smb:/")) { try { - jcifs.Config.registerSmbURLHandler(); URL a = new URL(path); String userinfo = a.getUserInfo(); if (userinfo != null) { diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java index 1f2f45a8d..c0fee761a 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java @@ -27,6 +27,7 @@ import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.GeneralSecurityException; +import java.util.concurrent.Callable; import com.afollestad.materialdialogs.MaterialDialog; import com.amaze.filemanager.R; diff --git a/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java b/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java new file mode 100644 index 000000000..8727edb67 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.utils.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface BlockingAsync {} diff --git a/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java b/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java new file mode 100644 index 000000000..a00604c79 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/*package com.amaze.filemanager.utils.aop; + +import android.util.Log; +import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +@Aspect +public class AsyncAspect { + + @Pointcut("within(com.amaze.filemanager..*)") + public void asyncBlockPointcut() {} + + @Around("asyncBlockPointcut() && @annotation(com.amaze.filemanager.utils.annotations.BlockingAsync)") + public Object asyncBlockAspect(ProceedingJoinPoint proceedingJoinPoint) { + try { + return Observable.just(proceedingJoinPoint.proceed()).subscribeOn(Schedulers.io()).blockingSingle(); + } catch (Throwable throwable) { + Log.w(getClass().getSimpleName(), "Failed to proceed to join point due to: {}", throwable); + return null; + } + } +}*/ diff --git a/build.gradle b/build.gradle index d386348fe..406c05c0c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,11 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.4' - classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4' + classpath 'com.android.tools.build:gradle:4.0.0' + classpath "com.hiya:jacoco-android:0.2" + + //AOP + classpath 'com.archinamon:android-gradle-aspectj:4.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -25,6 +28,9 @@ allprojects { jcenter() maven { url "https://jitpack.io" } maven { url "https://jcenter.bintray.com" } + maven { + url "https://plugins.gradle.org/m2/" + } mavenCentral() } tasks.withType(Test) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b0225133b..bab89a334 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip From 8cec57f9cb7c2466ce6cfe59fb048bc43d129237 Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Thu, 3 Sep 2020 03:22:15 +0530 Subject: [PATCH 04/10] 1942: Fix single blocking calls from db --- .../filemanager/application/AppConfig.java | 14 +- .../filemanager/database/CloudHandler.java | 37 ++- .../filemanager/database/CryptHandler.java | 20 +- .../filemanager/database/SortHandler.java | 10 +- .../filemanager/database/TabHandler.java | 17 +- .../filemanager/database/UtilsHandler.java | 228 +++++++++++++----- .../database/daos/BookmarkEntryDao.java | 6 +- .../database/daos/CloudEntryDao.java | 6 +- .../database/daos/EncryptedEntryDao.java | 6 +- .../database/daos/GridEntryDao.java | 7 +- .../database/daos/HiddenEntryDao.java | 7 +- .../database/daos/HistoryEntryDao.java | 7 +- .../database/daos/ListEntryDao.java | 5 +- .../database/daos/SftpEntryDao.java | 6 +- .../database/daos/SmbEntryDao.java | 6 +- .../filemanager/database/daos/SortDao.java | 1 + .../filemanager/database/daos/TabDao.java | 6 +- .../filemanager/filesystem/FileUtil.java | 7 +- .../filemanager/filesystem/HybridFile.java | 27 +-- .../filesystem/ssh/SshClientUtils.java | 4 +- .../ui/activities/MainActivity.java | 56 ++--- .../ui/dialogs/SmbConnectDialog.java | 1 - .../filemanager/utils/aop/AsyncAspect.java | 19 +- .../com/amaze/filemanager/test/TestUtils.java | 2 +- 24 files changed, 335 insertions(+), 170 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java index 7b4ab4147..87feccc0c 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -21,12 +21,7 @@ package com.amaze.filemanager.application; import java.lang.ref.WeakReference; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import android.util.Log; import com.amaze.filemanager.database.ExplorerDatabase; import com.amaze.filemanager.database.UtilitiesDatabase; import com.amaze.filemanager.database.UtilsHandler; @@ -41,8 +36,6 @@ import com.android.volley.toolbox.Volley; import android.app.Activity; import android.app.Application; import android.content.Context; -import android.os.AsyncTask; -import android.os.Handler; import android.os.StrictMode; import android.widget.Toast; @@ -50,6 +43,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatDelegate; + import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -103,7 +97,7 @@ public class AppConfig extends GlideApplication { super.onTerminate(); } -// ExecutorService executorService = Executors.newCachedThreadPool(); + // ExecutorService executorService = Executors.newCachedThreadPool(); /** * Post a runnable to handler. Use this in case we don't have any restriction to execute after @@ -118,8 +112,8 @@ public class AppConfig extends GlideApplication { } });*/ -// executorService.execute(runnable); -// Observable.just(runnable).subscribeOn(AndroidSchedulers.mainThread()).subscribe(); + // executorService.execute(runnable); + // Observable.just(runnable).subscribeOn(AndroidSchedulers.mainThread()).subscribe(); } /** diff --git a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java index 164a2695f..e6e7b67fe 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java @@ -20,7 +20,6 @@ package com.amaze.filemanager.database; -import java.util.Arrays; import java.util.List; import com.amaze.filemanager.database.models.explorer.CloudEntry; @@ -29,9 +28,10 @@ import com.amaze.filemanager.ui.fragments.CloudSheetFragment; import com.amaze.filemanager.utils.OpenMode; import android.content.Context; +import android.util.Log; import androidx.annotation.NonNull; -import io.reactivex.functions.Consumer; + import io.reactivex.schedulers.Schedulers; /** Created by vishal on 18/4/17. */ @@ -63,14 +63,17 @@ public class CloudHandler { } public void clear(OpenMode serviceType) { - database.cloudEntryDao().findByServiceType(serviceType.ordinal()).subscribeOn(Schedulers.io()).subscribe(new Consumer() { - @Override - public void accept(CloudEntry cloudEntry) { - database - .cloudEntryDao() - .delete(cloudEntry).subscribeOn(Schedulers.io()).subscribe(); - } - }); + database + .cloudEntryDao() + .findByServiceType(serviceType.ordinal()) + .subscribeOn(Schedulers.io()) + .subscribe( + cloudEntry -> + database + .cloudEntryDao() + .delete(cloudEntry) + .subscribeOn(Schedulers.io()) + .subscribe()); } public void updateEntry(OpenMode serviceType, CloudEntry newCloudEntry) @@ -85,12 +88,22 @@ public class CloudHandler { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - return database.cloudEntryDao().findByServiceType(serviceType.ordinal()).subscribeOn(Schedulers.io()).blockingGet(); + try { + return database + .cloudEntryDao() + .findByServiceType(serviceType.ordinal()) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } public List getAllEntries() throws CloudPluginException { if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException(); - return database.cloudEntryDao().list().subscribeOn(Schedulers.io()).toList().blockingGet(); + return database.cloudEntryDao().list().subscribeOn(Schedulers.io()).blockingGet(); } } diff --git a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java index daafe40a9..853b6c233 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CryptHandler.java @@ -20,13 +20,16 @@ package com.amaze.filemanager.database; +import java.util.List; + import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.models.explorer.EncryptedEntry; -import androidx.annotation.NonNull; -import io.reactivex.schedulers.Schedulers; +import android.util.Log; -import java.util.List; +import androidx.annotation.NonNull; + +import io.reactivex.schedulers.Schedulers; /** Created by vishal on 15/4/17. */ public class CryptHandler { @@ -59,11 +62,18 @@ public class CryptHandler { } public EncryptedEntry findEntry(String path) { - return database.encryptedEntryDao().select(path).subscribeOn(Schedulers.io()).blockingGet(); + try { + return database.encryptedEntryDao().select(path).subscribeOn(Schedulers.io()).blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } public EncryptedEntry[] getAllEntries() { - List encryptedEntryList = database.encryptedEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet(); + List encryptedEntryList = + database.encryptedEntryDao().list().subscribeOn(Schedulers.io()).blockingGet(); EncryptedEntry[] encryptedEntries = new EncryptedEntry[encryptedEntryList.size()]; return encryptedEntryList.toArray(encryptedEntries); } diff --git a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java index e88b5dabb..5720169ab 100644 --- a/app/src/main/java/com/amaze/filemanager/database/SortHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/SortHandler.java @@ -31,9 +31,11 @@ import com.amaze.filemanager.database.models.explorer.Sort; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import io.reactivex.schedulers.Schedulers; /** Created by Ning on 5/28/2018. */ @@ -84,6 +86,12 @@ public class SortHandler { @Nullable public Sort findEntry(String path) { - return database.sortDao().find(path).subscribeOn(Schedulers.io()).blockingGet(); + try { + return database.sortDao().find(path).subscribeOn(Schedulers.io()).blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } } diff --git a/app/src/main/java/com/amaze/filemanager/database/TabHandler.java b/app/src/main/java/com/amaze/filemanager/database/TabHandler.java index 8511e52f8..1a93facec 100644 --- a/app/src/main/java/com/amaze/filemanager/database/TabHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/TabHandler.java @@ -20,14 +20,17 @@ package com.amaze.filemanager.database; +import java.util.List; + import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.models.explorer.Tab; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import io.reactivex.schedulers.Schedulers; -import java.util.List; +import io.reactivex.schedulers.Schedulers; /** Created by Vishal on 9/17/2014. */ public class TabHandler { @@ -57,11 +60,17 @@ public class TabHandler { @Nullable public Tab findTab(int tabNo) { - return database.tabDao().find(tabNo).subscribeOn(Schedulers.io()).blockingGet(); + try { + return database.tabDao().find(tabNo).subscribeOn(Schedulers.io()).blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } public Tab[] getAllTabs() { - List tabList = database.tabDao().list().toList().subscribeOn(Schedulers.io()).blockingGet(); + List tabList = database.tabDao().list().subscribeOn(Schedulers.io()).blockingGet(); Tab[] tabs = new Tab[tabList.size()]; return tabList.toArray(tabs); } diff --git a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java index 5052cb0c1..adb640e20 100644 --- a/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/UtilsHandler.java @@ -24,7 +24,6 @@ import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -49,6 +48,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; + import io.reactivex.schedulers.Schedulers; /** @@ -83,41 +83,59 @@ public class UtilsHandler { public void saveToDatabase(OperationData operationData) { switch (operationData.type) { case HIDDEN: - utilitiesDatabase.hiddenEntryDao().insert(new Hidden(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .hiddenEntryDao() + .insert(new Hidden(operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case HISTORY: - utilitiesDatabase.historyEntryDao().insert(new History(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .historyEntryDao() + .insert(new History(operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case LIST: utilitiesDatabase - .listEntryDao() - .insert( - new com.amaze.filemanager.database.models.utilities.List( - operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + .listEntryDao() + .insert(new com.amaze.filemanager.database.models.utilities.List(operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case GRID: - utilitiesDatabase.gridEntryDao().insert(new Grid(operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .gridEntryDao() + .insert(new Grid(operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case BOOKMARKS: utilitiesDatabase - .bookmarkEntryDao() - .insert(new Bookmark(operationData.name, operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + .bookmarkEntryDao() + .insert(new Bookmark(operationData.name, operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case SMB: utilitiesDatabase - .smbEntryDao() - .insert(new SmbEntry(operationData.name, operationData.path)).subscribeOn(Schedulers.io()).subscribe(); + .smbEntryDao() + .insert(new SmbEntry(operationData.name, operationData.path)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case SFTP: utilitiesDatabase - .sftpEntryDao() - .insert( - new SftpEntry( - operationData.path, - operationData.name, - operationData.hostKey, - operationData.sshKeyName, - operationData.sshKey)).subscribeOn(Schedulers.io()).subscribe(); + .sftpEntryDao() + .insert( + new SftpEntry( + operationData.path, + operationData.name, + operationData.hostKey, + operationData.sshKeyName, + operationData.sshKey)) + .subscribeOn(Schedulers.io()) + .subscribe(); break; default: throw new IllegalStateException("Unidentified operation!"); @@ -127,16 +145,32 @@ public class UtilsHandler { public void removeFromDatabase(OperationData operationData) { switch (operationData.type) { case HIDDEN: - utilitiesDatabase.hiddenEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .hiddenEntryDao() + .deleteByPath(operationData.path) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case HISTORY: - utilitiesDatabase.historyEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .historyEntryDao() + .deleteByPath(operationData.path) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case LIST: - utilitiesDatabase.listEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .listEntryDao() + .deleteByPath(operationData.path) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case GRID: - utilitiesDatabase.gridEntryDao().deleteByPath(operationData.path).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .gridEntryDao() + .deleteByPath(operationData.path) + .subscribeOn(Schedulers.io()) + .subscribe(); break; case BOOKMARKS: removeBookmarksPath(operationData.name, operationData.path); @@ -177,23 +211,33 @@ public class UtilsHandler { String sshKeyName, String sshKey) { - SftpEntry entry = utilitiesDatabase.sftpEntryDao().findByName(oldConnectionName).subscribeOn(Schedulers.io()).blockingGet(); + utilitiesDatabase + .sftpEntryDao() + .findByName(oldConnectionName) + .subscribeOn(Schedulers.io()) + .subscribe( + entry -> { + entry.name = connectionName; + entry.path = path; + entry.hostKey = hostKey; - entry.name = connectionName; - entry.path = path; - entry.hostKey = hostKey; + if (sshKeyName != null && sshKey != null) { + entry.sshKeyName = sshKeyName; + entry.sshKey = sshKey; + } - if (sshKeyName != null && sshKey != null) { - entry.sshKeyName = sshKeyName; - entry.sshKey = sshKey; - } - - utilitiesDatabase.sftpEntryDao().update(entry).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .sftpEntryDao() + .update(entry) + .subscribeOn(Schedulers.io()) + .subscribe(); + }); } public LinkedList getHistoryLinkedList() { LinkedList paths = new LinkedList<>(); - for (History history : utilitiesDatabase.historyEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { + for (History history : + utilitiesDatabase.historyEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { paths.add(history.path); } return paths; @@ -203,24 +247,28 @@ public class UtilsHandler { ConcurrentRadixTree paths = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory()); - for (String path : utilitiesDatabase.hiddenEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()) { + for (String path : + utilitiesDatabase.hiddenEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet()) { paths.put(path, VoidValue.SINGLETON); } return paths; } public ArrayList getListViewList() { - return new ArrayList<>(utilitiesDatabase.listEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()); + return new ArrayList<>( + utilitiesDatabase.listEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getGridViewList() { - return new ArrayList<>(utilitiesDatabase.gridEntryDao().listPaths().toList().subscribeOn(Schedulers.io()).blockingGet()); + return new ArrayList<>( + utilitiesDatabase.gridEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getBookmarksList() { ArrayList row = new ArrayList<>(); - for (Bookmark bookmark : utilitiesDatabase.bookmarkEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { + for (Bookmark bookmark : + utilitiesDatabase.bookmarkEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { row.add(new String[] {bookmark.name, bookmark.path}); } return row; @@ -228,7 +276,8 @@ public class UtilsHandler { public ArrayList getSmbList() { ArrayList retval = new ArrayList(); - for (SmbEntry entry : utilitiesDatabase.smbEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { + for (SmbEntry entry : + utilitiesDatabase.smbEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { try { String path = SmbUtil.getSmbDecryptedPath(context, entry.path); @@ -249,7 +298,8 @@ public class UtilsHandler { public List getSftpList() { ArrayList retval = new ArrayList(); - for (SftpEntry entry : utilitiesDatabase.sftpEntryDao().list().toList().subscribeOn(Schedulers.io()).blockingGet()) { + for (SftpEntry entry : + utilitiesDatabase.sftpEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { String path = SshClientUtils.decryptSshPathAsNecessary(entry.path); if (path == null) { @@ -268,22 +318,56 @@ public class UtilsHandler { public String getSshHostKey(String uri) { uri = SshClientUtils.encryptSshPathAsNecessary(uri); if (uri != null) { - return utilitiesDatabase.sftpEntryDao().getSshHostKey(uri).subscribeOn(Schedulers.io()).blockingGet(); + try { + return utilitiesDatabase + .sftpEntryDao() + .getSshHostKey(uri) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } else { return null; } } public String getSshAuthPrivateKeyName(String uri) { - return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKeyName(uri).subscribeOn(Schedulers.io()).blockingGet(); + try { + return utilitiesDatabase + .sftpEntryDao() + .getSshAuthPrivateKeyName(uri) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } public String getSshAuthPrivateKey(String uri) { - return utilitiesDatabase.sftpEntryDao().getSshAuthPrivateKey(uri).subscribeOn(Schedulers.io()).blockingGet(); + try { + return utilitiesDatabase + .sftpEntryDao() + .getSshAuthPrivateKey(uri) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return null; + } } private void removeBookmarksPath(String name, String path) { - utilitiesDatabase.bookmarkEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .bookmarkEntryDao() + .deleteByNameAndPath(name, path) + .subscribeOn(Schedulers.io()) + .subscribe(); } /** @@ -293,17 +377,41 @@ public class UtilsHandler { * must encrypt it's password fiend first first */ private void removeSmbPath(String name, String path) { - if ("".equals(path)) utilitiesDatabase.smbEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); - else utilitiesDatabase.smbEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); + if ("".equals(path)) + utilitiesDatabase.smbEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); + else + utilitiesDatabase + .smbEntryDao() + .deleteByNameAndPath(name, path) + .subscribeOn(Schedulers.io()) + .subscribe(); } private void removeSftpPath(String name, String path) { - if ("".equals(path)) utilitiesDatabase.sftpEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); - else utilitiesDatabase.sftpEntryDao().deleteByNameAndPath(name, path).subscribeOn(Schedulers.io()).subscribe(); + if ("".equals(path)) + utilitiesDatabase.sftpEntryDao().deleteByName(name).subscribeOn(Schedulers.io()).subscribe(); + else + utilitiesDatabase + .sftpEntryDao() + .deleteByNameAndPath(name, path) + .subscribeOn(Schedulers.io()) + .subscribe(); } public void renameBookmark(String oldName, String oldPath, String newName, String newPath) { - Bookmark bookmark = utilitiesDatabase.bookmarkEntryDao().findByNameAndPath(oldName, oldPath).subscribeOn(Schedulers.io()).blockingGet(); + Bookmark bookmark; + try { + bookmark = + utilitiesDatabase + .bookmarkEntryDao() + .findByNameAndPath(oldName, oldPath) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + // catch error to handle Single#onError for blockingGet + Log.e(getClass().getSimpleName(), e.getMessage()); + return; + } bookmark.name = newName; bookmark.path = newPath; @@ -317,12 +425,22 @@ public class UtilsHandler { } catch (GeneralSecurityException | IOException e) { Log.e(TAG, "Error encrypting SMB path", e); } + String finalNewPath = newPath; + utilitiesDatabase + .smbEntryDao() + .findByNameAndPath(oldName, oldPath) + .subscribeOn(Schedulers.io()) + .subscribe( + smbEntry -> { + smbEntry.name = newName; + smbEntry.path = finalNewPath; - SmbEntry smbEntry = utilitiesDatabase.smbEntryDao().findByNameAndPath(oldName, oldPath).subscribeOn(Schedulers.io()).blockingGet(); - smbEntry.name = newName; - smbEntry.path = newPath; - - utilitiesDatabase.smbEntryDao().update(smbEntry).subscribeOn(Schedulers.io()).subscribe(); + utilitiesDatabase + .smbEntryDao() + .update(smbEntry) + .subscribeOn(Schedulers.io()) + .subscribe(); + }); } public void clearTable(Operation table) { diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java index 5987fb400..94fc4ba54 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/BookmarkEntryDao.java @@ -24,14 +24,16 @@ import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_NAME; import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_BOOKMARKS; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.Bookmark; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -52,7 +54,7 @@ public interface BookmarkEntryDao { Completable update(Bookmark instance); @Query("SELECT * FROM " + TABLE_BOOKMARKS) - Observable list(); + Single> list(); @Query( "SELECT * FROM " diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java index 8a322ce72..2cd8fd767 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/CloudEntryDao.java @@ -23,6 +23,8 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.ExplorerDatabase.COLUMN_CLOUD_SERVICE; import static com.amaze.filemanager.database.ExplorerDatabase.TABLE_CLOUD_PERSIST; +import java.util.List; + import com.amaze.filemanager.database.models.explorer.CloudEntry; import androidx.room.Dao; @@ -30,8 +32,8 @@ import androidx.room.Delete; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -53,7 +55,7 @@ public interface CloudEntryDao { Single findByServiceType(int serviceType); @Query("SELECT * FROM " + TABLE_CLOUD_PERSIST) - Observable list(); + Single> list(); @Update Completable update(CloudEntry entry); diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java index 875bfe6c4..824a52cad 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/EncryptedEntryDao.java @@ -23,6 +23,8 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.ExplorerDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.ExplorerDatabase.TABLE_ENCRYPTED; +import java.util.List; + import com.amaze.filemanager.database.models.explorer.EncryptedEntry; import androidx.room.Dao; @@ -30,8 +32,8 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -59,5 +61,5 @@ public interface EncryptedEntryDao { Completable delete(String path); @Query("SELECT * FROM " + TABLE_ENCRYPTED) - Observable list(); + Single> list(); } diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java index 6701dc69b..57b753d92 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/GridEntryDao.java @@ -23,14 +23,17 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_GRID; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.Grid; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link Grid}. Concrete class is generated by Room during @@ -50,7 +53,7 @@ public interface GridEntryDao { Completable update(Grid instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_GRID) - Observable listPaths(); + Single> listPaths(); @Query("DELETE FROM " + TABLE_GRID + " WHERE " + COLUMN_PATH + " = :path") Completable deleteByPath(String path); diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java index 382e1d028..03442cb6f 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/HiddenEntryDao.java @@ -23,14 +23,17 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_HIDDEN; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.Hidden; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link Hidden}. Concrete class is generated by Room during @@ -50,7 +53,7 @@ public interface HiddenEntryDao { Completable update(Hidden instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_HIDDEN) - Observable listPaths(); + Single> listPaths(); @Query("DELETE FROM " + TABLE_HIDDEN + " WHERE " + COLUMN_PATH + " = :path") Completable deleteByPath(String path); diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java index c4e2554ec..d1c791e39 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/HistoryEntryDao.java @@ -23,6 +23,8 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_HISTORY; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.History; import androidx.room.Dao; @@ -30,8 +32,9 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link History}. Concrete class is generated by Room during @@ -51,7 +54,7 @@ public interface HistoryEntryDao { Completable update(History instance); @Query("SELECT * FROM " + TABLE_HISTORY) - Observable list(); + Single> list(); @Query("DELETE FROM " + TABLE_HISTORY + " WHERE " + COLUMN_PATH + " = :path") Completable deleteByPath(String path); diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java index 6ddd82305..417360c56 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/ListEntryDao.java @@ -30,8 +30,9 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; +import io.reactivex.Single; /** * {@link Dao} interface definition for {@link List}. Concrete class is generated by Room during @@ -51,7 +52,7 @@ public interface ListEntryDao { Completable update(List instance); @Query("SELECT " + COLUMN_PATH + " FROM " + TABLE_LIST) - Observable listPaths(); + Single> listPaths(); @Query("DELETE FROM " + TABLE_LIST + " WHERE " + COLUMN_PATH + " = :path") Completable deleteByPath(String path); diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java index 6c9eb49f3..eec4d6807 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SftpEntryDao.java @@ -27,14 +27,16 @@ import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PRIVATE_KE import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PRIVATE_KEY_NAME; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_SFTP; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.SftpEntry; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -55,7 +57,7 @@ public interface SftpEntryDao { Completable update(SftpEntry instance); @Query("SELECT * FROM " + TABLE_SFTP) - Observable list(); + Single> list(); @Query( "SELECT * FROM " diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java index 8fc79a802..1b6b76b12 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SmbEntryDao.java @@ -24,14 +24,16 @@ import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_NAME; import static com.amaze.filemanager.database.UtilitiesDatabase.COLUMN_PATH; import static com.amaze.filemanager.database.UtilitiesDatabase.TABLE_SMB; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.SmbEntry; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -52,7 +54,7 @@ public interface SmbEntryDao { Completable update(SmbEntry instance); @Query("SELECT * FROM " + TABLE_SMB) - Observable list(); + Single> list(); @Query( "SELECT * FROM " diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java index 749c895e7..21f53b781 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/SortDao.java @@ -30,6 +30,7 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Update; + import io.reactivex.Completable; import io.reactivex.Single; diff --git a/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java b/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java index 9714407e4..f19925644 100644 --- a/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java +++ b/app/src/main/java/com/amaze/filemanager/database/daos/TabDao.java @@ -23,6 +23,8 @@ package com.amaze.filemanager.database.daos; import static com.amaze.filemanager.database.ExplorerDatabase.COLUMN_TAB_NO; import static com.amaze.filemanager.database.ExplorerDatabase.TABLE_TAB; +import java.util.List; + import com.amaze.filemanager.database.models.explorer.Tab; import androidx.room.Dao; @@ -30,8 +32,8 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Transaction; + import io.reactivex.Completable; -import io.reactivex.Observable; import io.reactivex.Single; /** @@ -55,5 +57,5 @@ public interface TabDao { Single find(int tabNo); @Query("SELECT * FROM " + TABLE_TAB) - Observable list(); + Single> list(); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index 575ea1865..125ba30e2 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -285,9 +285,7 @@ public abstract class FileUtil { } else { OutputStream outputStream = targetSmbFile.getOutputStream(); bufferedOutputStream = new BufferedOutputStream(outputStream); - retval.add( - mainActivity.mainActivityHelper.parseSmbPath( - targetSmbFile.getPath())); + retval.add(HybridFile.parseSmbPath(targetSmbFile.getPath())); } break; case SFTP: @@ -380,7 +378,8 @@ public abstract class FileUtil { emitter.onError(new Exception()); } }) - .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) .subscribe( paths -> { if (paths != null) { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 3e3237ac0..872ffc97b 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -20,13 +20,16 @@ package com.amaze.filemanager.filesystem; -import android.content.ContentResolver; -import android.content.Context; -import android.os.Build; -import android.preference.PreferenceManager; -import android.util.Log; -import androidx.annotation.NonNull; -import androidx.documentfile.provider.DocumentFile; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.EnumSet; + import com.amaze.filemanager.adapters.data.LayoutElementParcelable; import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.CloudHandler; @@ -68,16 +71,6 @@ import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.sftp.SFTPClient; import net.schmizz.sshj.sftp.SFTPException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.EnumSet; - /** Created by Arpit on 07-07-2015. */ // Hybrid file for handeling all types of files public class HybridFile { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java index 8962883ba..92f62f32a 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.util.List; -import java.util.concurrent.Callable; import com.amaze.filemanager.R; import com.amaze.filemanager.application.AppConfig; @@ -76,7 +75,8 @@ public abstract class SshClientUtils { T retval = null; if (client != null) { try { - retval = (T) Single.just(template.execute(client)).subscribeOn(Schedulers.io()).blockingGet(); + retval = + (T) Single.just(template.execute(client)).subscribeOn(Schedulers.io()).blockingGet(); } catch (Exception e) { Log.e(TAG, "Error executing template method", e); } finally { diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 4af661764..75b31a19f 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -155,7 +155,6 @@ import androidx.loader.content.Loader; import eu.chainfire.libsuperuser.Shell; import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.functions.Action; import io.reactivex.schedulers.Schedulers; public class MainActivity extends PermissionsActivity @@ -308,7 +307,7 @@ public class MainActivity extends PermissionsActivity if (CloudSheetFragment.isCloudProviderAvailable(this)) { - getSupportLoaderManager().initLoader(REQUEST_CODE_CLOUD_LIST_KEYS, null, this); + // LoaderManager.getInstance(this).initLoader(REQUEST_CODE_CLOUD_LIST_KEYS, null, this); } path = getIntent().getStringExtra("path"); @@ -373,29 +372,23 @@ public class MainActivity extends PermissionsActivity checkForExternalPermission(); Completable.fromRunnable( - new Runnable() { - @Override - public void run() { - dataUtils.setHiddenFiles(utilsHandler.getHiddenFilesConcurrentRadixTree()); - dataUtils.setHistory(utilsHandler.getHistoryLinkedList()); - dataUtils.setGridfiles(utilsHandler.getGridViewList()); - dataUtils.setListfiles(utilsHandler.getListViewList()); - dataUtils.setBooks(utilsHandler.getBookmarksList()); - ArrayList servers = new ArrayList(); - servers.addAll(utilsHandler.getSmbList()); - servers.addAll(utilsHandler.getSftpList()); - dataUtils.setServers(servers); - } + () -> { + dataUtils.setHiddenFiles(utilsHandler.getHiddenFilesConcurrentRadixTree()); + dataUtils.setHistory(utilsHandler.getHistoryLinkedList()); + dataUtils.setGridfiles(utilsHandler.getGridViewList()); + dataUtils.setListfiles(utilsHandler.getListViewList()); + dataUtils.setBooks(utilsHandler.getBookmarksList()); + ArrayList servers = new ArrayList<>(); + servers.addAll(utilsHandler.getSmbList()); + servers.addAll(utilsHandler.getSftpList()); + dataUtils.setServers(servers); }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - new Action() { - @Override - public void run() { - drawer.refreshDrawer(); - invalidateFragmentAndBundle(savedInstanceState); - } + () -> { + drawer.refreshDrawer(); + invalidateFragmentAndBundle(savedInstanceState); }); } @@ -404,7 +397,7 @@ public class MainActivity extends PermissionsActivity if (openProcesses) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace( - R.id.content_frame, new ProcessViewerFragment(), KEY_INTENT_PROCESS_VIEWER); + R.id.content_frame, new ProcessViewerFragment(), KEY_INTENT_PROCESS_VIEWER); // transaction.addToBackStack(null); drawer.setSomethingSelected(true); openProcesses = false; @@ -413,16 +406,16 @@ public class MainActivity extends PermissionsActivity transaction.commit(); supportInvalidateOptionsMenu(); } else if (intent.getAction() != null - && intent.getAction().equals(TileService.ACTION_QS_TILE_PREFERENCES)) { + && intent.getAction().equals(TileService.ACTION_QS_TILE_PREFERENCES)) { // tile preferences, open ftp fragment FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction(); transaction2.replace(R.id.content_frame, new FtpServerFragment()); appBarLayout - .animate() - .translationY(0) - .setInterpolator(new DecelerateInterpolator(2)) - .start(); + .animate() + .translationY(0) + .setInterpolator(new DecelerateInterpolator(2)) + .start(); drawer.setSomethingSelected(true); drawer.deselectEverything(); @@ -613,10 +606,11 @@ public class MainActivity extends PermissionsActivity data.append(AppConstants.NEW_LINE).append(extras.getString(Intent.EXTRA_TEXT)); } String fileName = Long.toString(System.currentTimeMillis()); - AppConfig.runInBackground( - () -> - FileUtil.mktextfile( - data.toString(), getCurrentMainFragment().getCurrentPath(), fileName)); + AppConfig.getInstance() + .runInBackground( + () -> + FileUtil.mktextfile( + data.toString(), getCurrentMainFragment().getCurrentPath(), fileName)); } public void clearFabActionItems() { diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java index c0fee761a..1f2f45a8d 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java @@ -27,7 +27,6 @@ import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.GeneralSecurityException; -import java.util.concurrent.Callable; import com.afollestad.materialdialogs.MaterialDialog; import com.amaze.filemanager.R; diff --git a/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java b/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java index a00604c79..e5fc25325 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java +++ b/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java @@ -18,29 +18,34 @@ * along with this program. If not, see . */ -/*package com.amaze.filemanager.utils.aop; +package com.amaze.filemanager.utils.aop; -import android.util.Log; -import io.reactivex.Observable; -import io.reactivex.schedulers.Schedulers; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; +import android.util.Log; + +import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; + @Aspect public class AsyncAspect { @Pointcut("within(com.amaze.filemanager..*)") public void asyncBlockPointcut() {} - @Around("asyncBlockPointcut() && @annotation(com.amaze.filemanager.utils.annotations.BlockingAsync)") + @Around( + "asyncBlockPointcut() && @annotation(com.amaze.filemanager.utils.annotations.BlockingAsync)") public Object asyncBlockAspect(ProceedingJoinPoint proceedingJoinPoint) { try { - return Observable.just(proceedingJoinPoint.proceed()).subscribeOn(Schedulers.io()).blockingSingle(); + return Observable.just(proceedingJoinPoint.proceed()) + .subscribeOn(Schedulers.io()) + .blockingSingle(); } catch (Throwable throwable) { Log.w(getClass().getSimpleName(), "Failed to proceed to join point due to: {}", throwable); return null; } } -}*/ +} diff --git a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java index 88f098d78..61e1c2418 100644 --- a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java +++ b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java @@ -20,8 +20,8 @@ package com.amaze.filemanager.test; -import static org.awaitility.Awaitility.await; import static android.os.Build.VERSION_CODES.P; +import static org.awaitility.Awaitility.await; import static org.robolectric.Shadows.shadowOf; import java.io.File; From c6f93d6bc75e84454bb267257e5b3f922028d796 Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Thu, 3 Sep 2020 03:29:05 +0530 Subject: [PATCH 05/10] 1942: Refactor code --- .../com/amaze/filemanager/application/AppConfig.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java index 87feccc0c..0579fe5a8 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -97,8 +97,6 @@ public class AppConfig extends GlideApplication { super.onTerminate(); } - // ExecutorService executorService = Executors.newCachedThreadPool(); - /** * Post a runnable to handler. Use this in case we don't have any restriction to execute after * this runnable is executed, and {@link #runInBackground(Runnable)} in case we need to execute @@ -106,14 +104,6 @@ public class AppConfig extends GlideApplication { */ public void runInBackground(Runnable runnable) { Completable.fromRunnable(runnable).subscribeOn(Schedulers.io()).subscribe(); - /*Single.just(runnable).subscribeOn(Schedulers.io()).subscribe(new Consumer() { - @Override - public void accept(Runnable runnable) throws Throwable { - - } - });*/ - // executorService.execute(runnable); - // Observable.just(runnable).subscribeOn(AndroidSchedulers.mainThread()).subscribe(); } /** @@ -170,7 +160,7 @@ public class AppConfig extends GlideApplication { * @param r Runnable to run */ public void runInApplicationThread(Runnable r) { - Completable.fromRunnable(r).subscribeOn(AndroidSchedulers.mainThread()).cache(); + Completable.fromRunnable(r).subscribeOn(AndroidSchedulers.mainThread()).subscribe(); } public static synchronized AppConfig getInstance() { From 4a07c24df8bad020919dfdb91b1592aadacd0a1a Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Mon, 7 Sep 2020 03:10:19 +0530 Subject: [PATCH 06/10] 1942: Remove aop implementation; fix single callable for HybridFile#isDirectory --- app/build.gradle | 4 +- .../filemanager/filesystem/HybridFile.java | 15 ++++-- .../filemanager/filesystem/PasteHelper.java | 47 ++++++++++------- .../filesystem/ssh/SshClientUtils.java | 5 +- .../ui/activities/MainActivity.java | 2 +- .../utils/annotations/BlockingAsync.java | 30 ----------- .../filemanager/utils/aop/AsyncAspect.java | 51 ------------------- build.gradle | 10 +--- gradle/wrapper/gradle-wrapper.properties | 2 +- 9 files changed, 49 insertions(+), 117 deletions(-) delete mode 100644 app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java delete mode 100644 app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java diff --git a/app/build.gradle b/app/build.gradle index be69ed9be..4ee9e838a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.application' -apply plugin: "com.hiya.jacoco-android" -apply plugin: 'com.archinamon.aspectj' +apply plugin: 'jacoco-android' android { compileSdkVersion 28 @@ -117,7 +116,6 @@ dependencies { annotationProcessor "androidx.room:room-compiler:$roomVersion" // RxJava support for Room implementation "androidx.room:room-rxjava2:$roomVersion" - implementation 'com.android.billingclient:billing:2.1.0' annotationProcessor 'androidx.annotation:annotation:1.1.0' //For tests diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 872ffc97b..82b367e8b 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -47,7 +47,6 @@ import com.amaze.filemanager.utils.OTGUtil; import com.amaze.filemanager.utils.OnFileFound; import com.amaze.filemanager.utils.OpenMode; import com.amaze.filemanager.utils.RootUtils; -import com.amaze.filemanager.utils.annotations.*; import com.cloudrail.si.interfaces.CloudStorage; import com.cloudrail.si.types.SpaceAllocation; @@ -61,6 +60,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; +import io.reactivex.Single; +import io.reactivex.schedulers.Schedulers; import jcifs.smb.SmbException; import jcifs.smb.SmbFile; import net.schmizz.sshj.SSHClient; @@ -438,7 +439,6 @@ public class HybridFile { return isDirectory; } - @BlockingAsync public boolean isDirectory(Context context) { boolean isDirectory; switch (mode) { @@ -453,14 +453,21 @@ public class HybridFile { .getType() .equals(FileMode.Type.DIRECTORY); } catch (SFTPException notFound) { + Log.e( + getClass().getSimpleName(), + "Fail to execute isDirectory for SFTP path :" + path); + notFound.printStackTrace(); return false; } } }); case SMB: try { - isDirectory = new SmbFile(path).isDirectory(); - } catch (MalformedURLException | SmbException e) { + isDirectory = + Single.fromCallable(() -> new SmbFile(path).isDirectory()) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { isDirectory = false; if (e.getCause() != null) e.getCause().printStackTrace(); else e.printStackTrace(); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java index 5b25001d4..b5b1c985a 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java @@ -37,6 +37,10 @@ import android.os.Parcelable; import android.text.Html; import android.text.Spanned; +import io.reactivex.Single; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + /** * Special immutable class for handling cut/copy operations. * @@ -129,26 +133,33 @@ public final class PasteHelper implements Parcelable { } private void showSnackbar() { - snackbar = - Utils.showThemedSnackbar( - mainActivity, - getSnackbarContent(), - BaseTransientBottomBar.LENGTH_INDEFINITE, - R.string.paste, - () -> { - String path = mainActivity.getCurrentMainFragment().getCurrentPath(); - ArrayList arrayList = new ArrayList<>(Arrays.asList(paths)); - boolean move = operation == PasteHelper.OPERATION_CUT; - new PrepareCopyTask( - mainActivity.getCurrentMainFragment(), - path, - move, + Single.fromCallable(() -> getSnackbarContent()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + spanned -> { + snackbar = + Utils.showThemedSnackbar( mainActivity, - mainActivity.isRootExplorer()) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arrayList); - dismissSnackbar(true); + spanned, + BaseTransientBottomBar.LENGTH_INDEFINITE, + R.string.paste, + () -> { + String path = mainActivity.getCurrentMainFragment().getCurrentPath(); + ArrayList arrayList = + new ArrayList<>(Arrays.asList(paths)); + boolean move = operation == PasteHelper.OPERATION_CUT; + new PrepareCopyTask( + mainActivity.getCurrentMainFragment(), + path, + move, + mainActivity, + mainActivity.isRootExplorer()) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arrayList); + dismissSnackbar(true); + }); + Utils.invalidateFab(mainActivity, () -> dismissSnackbar(true), true); }); - Utils.invalidateFab(mainActivity, () -> dismissSnackbar(true), true); } private Spanned getSnackbarContent() { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java index 92f62f32a..76a96d532 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshClientUtils.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.util.List; +import java.util.concurrent.Callable; import com.amaze.filemanager.R; import com.amaze.filemanager.application.AppConfig; @@ -76,7 +77,9 @@ public abstract class SshClientUtils { if (client != null) { try { retval = - (T) Single.just(template.execute(client)).subscribeOn(Schedulers.io()).blockingGet(); + Single.fromCallable((Callable) () -> template.execute(client)) + .subscribeOn(Schedulers.io()) + .blockingGet(); } catch (Exception e) { Log.e(TAG, "Error executing template method", e); } finally { diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 75b31a19f..c8a75f256 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -307,7 +307,7 @@ public class MainActivity extends PermissionsActivity if (CloudSheetFragment.isCloudProviderAvailable(this)) { - // LoaderManager.getInstance(this).initLoader(REQUEST_CODE_CLOUD_LIST_KEYS, null, this); + LoaderManager.getInstance(this).initLoader(REQUEST_CODE_CLOUD_LIST_KEYS, null, this); } path = getIntent().getStringExtra("path"); diff --git a/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java b/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java deleted file mode 100644 index 8727edb67..000000000 --- a/app/src/main/java/com/amaze/filemanager/utils/annotations/BlockingAsync.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.utils.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface BlockingAsync {} diff --git a/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java b/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java deleted file mode 100644 index e5fc25325..000000000 --- a/app/src/main/java/com/amaze/filemanager/utils/aop/AsyncAspect.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.utils.aop; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; - -import android.util.Log; - -import io.reactivex.Observable; -import io.reactivex.schedulers.Schedulers; - -@Aspect -public class AsyncAspect { - - @Pointcut("within(com.amaze.filemanager..*)") - public void asyncBlockPointcut() {} - - @Around( - "asyncBlockPointcut() && @annotation(com.amaze.filemanager.utils.annotations.BlockingAsync)") - public Object asyncBlockAspect(ProceedingJoinPoint proceedingJoinPoint) { - try { - return Observable.just(proceedingJoinPoint.proceed()) - .subscribeOn(Schedulers.io()) - .blockingSingle(); - } catch (Throwable throwable) { - Log.w(getClass().getSimpleName(), "Failed to proceed to join point due to: {}", throwable); - return null; - } - } -} diff --git a/build.gradle b/build.gradle index 406c05c0c..d386348fe 100644 --- a/build.gradle +++ b/build.gradle @@ -6,11 +6,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' - classpath "com.hiya:jacoco-android:0.2" - - //AOP - classpath 'com.archinamon:android-gradle-aspectj:4.2.1' + classpath 'com.android.tools.build:gradle:3.6.4' + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -28,9 +25,6 @@ allprojects { jcenter() maven { url "https://jitpack.io" } maven { url "https://jcenter.bintray.com" } - maven { - url "https://plugins.gradle.org/m2/" - } mavenCentral() } tasks.withType(Test) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bab89a334..b0225133b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip From 4184bd2e0ed865721a65f583987ec024e5443093 Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Mon, 7 Sep 2020 03:16:01 +0530 Subject: [PATCH 07/10] 1942: Refactor code --- .../filemanager/filesystem/FileUtil.java | 1 - .../filemanager/filesystem/PasteHelper.java | 55 +++++++++++-------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index 125ba30e2..fc846b363 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -368,7 +368,6 @@ public abstract class FileUtil { } } catch (IOException e) { e.printStackTrace(); - return; } } } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java index b5b1c985a..e5a202a09 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java @@ -38,7 +38,9 @@ import android.text.Html; import android.text.Spanned; import io.reactivex.Single; +import io.reactivex.SingleObserver; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; /** @@ -137,28 +139,37 @@ public final class PasteHelper implements Parcelable { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - spanned -> { - snackbar = - Utils.showThemedSnackbar( - mainActivity, - spanned, - BaseTransientBottomBar.LENGTH_INDEFINITE, - R.string.paste, - () -> { - String path = mainActivity.getCurrentMainFragment().getCurrentPath(); - ArrayList arrayList = - new ArrayList<>(Arrays.asList(paths)); - boolean move = operation == PasteHelper.OPERATION_CUT; - new PrepareCopyTask( - mainActivity.getCurrentMainFragment(), - path, - move, - mainActivity, - mainActivity.isRootExplorer()) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arrayList); - dismissSnackbar(true); - }); - Utils.invalidateFab(mainActivity, () -> dismissSnackbar(true), true); + new SingleObserver() { + @Override + public void onSubscribe(Disposable d) {} + + @Override + public void onSuccess(Spanned spanned) { + snackbar = + Utils.showThemedSnackbar( + mainActivity, + spanned, + BaseTransientBottomBar.LENGTH_INDEFINITE, + R.string.paste, + () -> { + String path = mainActivity.getCurrentMainFragment().getCurrentPath(); + ArrayList arrayList = + new ArrayList<>(Arrays.asList(paths)); + boolean move = operation == PasteHelper.OPERATION_CUT; + new PrepareCopyTask( + mainActivity.getCurrentMainFragment(), + path, + move, + mainActivity, + mainActivity.isRootExplorer()) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arrayList); + dismissSnackbar(true); + }); + Utils.invalidateFab(mainActivity, () -> dismissSnackbar(true), true); + } + + @Override + public void onError(Throwable e) {} }); } From 2f8a580d9f098ed383efa8e19055a3e0e0fdb433 Mon Sep 17 00:00:00 2001 From: VishalNehra Date: Mon, 7 Sep 2020 03:28:32 +0530 Subject: [PATCH 08/10] 1942: Refactor code --- .../filemanager/filesystem/FileUtil.java | 21 +++++++++++++++++-- .../filemanager/filesystem/PasteHelper.java | 8 ++++++- .../ui/activities/MainActivity.java | 19 ++++++++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index fc846b363..bad314ebf 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -72,8 +72,10 @@ import androidx.annotation.NonNull; import androidx.documentfile.provider.DocumentFile; import io.reactivex.Maybe; +import io.reactivex.MaybeObserver; import io.reactivex.MaybeOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import jcifs.smb.SmbException; import jcifs.smb.SmbFile; @@ -380,8 +382,12 @@ public abstract class FileUtil { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - paths -> { - if (paths != null) { + new MaybeObserver>() { + @Override + public void onSubscribe(Disposable d) {} + + @Override + public void onSuccess(List paths) { if (paths.size() == 1) { Toast.makeText( mainActivity, @@ -396,6 +402,17 @@ public abstract class FileUtil { .show(); } } + + @Override + public void onError(Throwable e) { + Log.e( + getClass().getSimpleName(), + "Failed to write uri to storage due to " + e.getCause()); + e.printStackTrace(); + } + + @Override + public void onComplete() {} }); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java index e5a202a09..e7409df10 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java @@ -36,6 +36,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.Html; import android.text.Spanned; +import android.util.Log; import io.reactivex.Single; import io.reactivex.SingleObserver; @@ -169,7 +170,12 @@ public final class PasteHelper implements Parcelable { } @Override - public void onError(Throwable e) {} + public void onError(Throwable e) { + Log.e( + getClass().getSimpleName(), + "Failed to show paste snackbar due to " + e.getCause()); + e.printStackTrace(); + } }); } diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index c8a75f256..a86f48789 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -154,7 +154,9 @@ import androidx.loader.content.Loader; import eu.chainfire.libsuperuser.Shell; import io.reactivex.Completable; +import io.reactivex.CompletableObserver; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; public class MainActivity extends PermissionsActivity @@ -386,9 +388,20 @@ public class MainActivity extends PermissionsActivity .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - () -> { - drawer.refreshDrawer(); - invalidateFragmentAndBundle(savedInstanceState); + new CompletableObserver() { + @Override + public void onSubscribe(Disposable d) {} + + @Override + public void onComplete() { + drawer.refreshDrawer(); + invalidateFragmentAndBundle(savedInstanceState); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } }); } From 8e982a7dc4c5a6a92da441aebb7a3d19fb5a76e4 Mon Sep 17 00:00:00 2001 From: TranceLove Date: Tue, 8 Sep 2020 13:50:22 +0800 Subject: [PATCH 09/10] Test fix: remove TestUtils.flushAppConfigHandlerThread() Fix failing test cases that rely on flushing AppConfig's ExecutorService for running background tasks. Instead, forcing RxJava's IO thread scheduler to become Schedulers.trampoline() which will force asynchronous code to run synchronously. Reference: https://stackoverflow.com/questions/46549405/testing-asynchronous-rxjava-code-android --- .../filesystem/ssh/SshConnectionPoolTest.java | 7 ++-- .../filemanager/test/ShadowCryptUtilTest.java | 12 +++++- .../com/amaze/filemanager/test/TestUtils.java | 39 ------------------- .../ui/activities/MainActivityTest.java | 10 +++++ 4 files changed, 24 insertions(+), 44 deletions(-) diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPoolTest.java b/app/src/test/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPoolTest.java index 8db0cccff..dd396521a 100644 --- a/app/src/test/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPoolTest.java +++ b/app/src/test/java/com/amaze/filemanager/filesystem/ssh/SshConnectionPoolTest.java @@ -42,13 +42,14 @@ import com.amaze.filemanager.database.models.OperationData; import com.amaze.filemanager.filesystem.ssh.test.TestKeyProvider; import com.amaze.filemanager.shadows.ShadowMultiDex; import com.amaze.filemanager.test.ShadowCryptUtil; -import com.amaze.filemanager.test.TestUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.reactivex.plugins.RxJavaPlugins; +import io.reactivex.schedulers.Schedulers; import net.schmizz.sshj.common.SecurityUtils; @RunWith(AndroidJUnit4.class) @@ -67,6 +68,8 @@ public class SshConnectionPoolTest { public static void bootstrap() throws Exception { hostKeyProvider = new TestKeyProvider(); userKeyProvider = new TestKeyProvider(); + RxJavaPlugins.reset(); + RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); } @After @@ -341,7 +344,5 @@ public class SshConnectionPoolTest { SecurityUtils.getFingerprint(hostKeyProvider.getKeyPair().getPublic()), "id_rsa", privateKeyContents)); - - TestUtils.flushAppConfigHandlerThread(); } } diff --git a/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java b/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java index 71e8c1de1..004087bef 100644 --- a/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java +++ b/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.util.concurrent.TimeUnit; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @@ -41,10 +42,19 @@ import com.amaze.filemanager.shadows.ShadowMultiDex; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.reactivex.plugins.RxJavaPlugins; +import io.reactivex.schedulers.Schedulers; + @RunWith(AndroidJUnit4.class) @Config(shadows = {ShadowMultiDex.class, ShadowCryptUtil.class}) public class ShadowCryptUtilTest { + @BeforeClass + public static void setUpBeforeClass() { + RxJavaPlugins.reset(); + RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); + } + @Test public void testEncryptDecrypt() throws GeneralSecurityException, IOException { String text = "test"; @@ -73,8 +83,6 @@ public class ShadowCryptUtilTest { null, null)); - TestUtils.flushAppConfigHandlerThread(); - await() .atMost(10, TimeUnit.SECONDS) .until( diff --git a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java index 61e1c2418..a2c8f025d 100644 --- a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java +++ b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java @@ -21,21 +21,14 @@ package com.amaze.filemanager.test; import static android.os.Build.VERSION_CODES.P; -import static org.awaitility.Awaitility.await; import static org.robolectric.Shadows.shadowOf; import java.io.File; -import java.lang.reflect.Field; -import java.util.concurrent.ExecutorService; import org.robolectric.shadows.ShadowStorageManager; -import org.robolectric.util.Scheduler; - -import com.amaze.filemanager.application.AppConfig; import android.os.Build; import android.os.Environment; -import android.os.Handler; import android.os.Parcel; import android.os.UserHandle; import android.os.storage.StorageManager; @@ -46,38 +39,6 @@ import androidx.test.core.app.ApplicationProvider; public class TestUtils { - /** - * Flush the background handler thread. - * - *

Due to the way Robolectric implement threads, Runnables posted to the handler thread via - * {@link AppConfig#runInBackground(Runnable)} may not be executed, making tests involving it will - * fail. This method accesses the {@link Scheduler} behind the {@link - * org.robolectric.shadows.ShadowLooper} of the background {@link Handler} and execute any {@link - * Runnable}s posted to it. - * - *

This method throws {@link AssertionError} directly so test code don't need to handle - * possible exceptions occur when accessing - * - *

AppConfig.backgroundHandler
- * - * via reflection. - * - * @see Scheduler#advanceToNextPostedRunnable() - * @see AppConfig#executorService - * @see AppConfig#runInBackground(Runnable) - */ - public static void flushAppConfigHandlerThread() { - try { - Field f = AppConfig.class.getDeclaredField("executorService"); - f.setAccessible(true); - ExecutorService h = (ExecutorService) f.get(AppConfig.getInstance()); - h.shutdown(); - await().until(h::isTerminated); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new AssertionError("Unable to access backgroundHandler within AppConfig"); - } - } - /** * Populate "internal device storage" to StorageManager with directory as provided by Robolectric. * diff --git a/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.java b/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.java index 1197dbe82..36413585d 100644 --- a/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.java +++ b/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @@ -56,6 +57,9 @@ import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.reactivex.plugins.RxJavaPlugins; +import io.reactivex.schedulers.Schedulers; + @RunWith(AndroidJUnit4.class) @Config( shadows = { @@ -72,6 +76,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; @LooperMode(LooperMode.Mode.PAUSED) public class MainActivityTest { + @BeforeClass + public static void setUpBeforeClass() { + RxJavaPlugins.reset(); + RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); + } + @Before public void setUp() { if (Build.VERSION.SDK_INT >= N) TestUtils.initializeInternalStorage(); From 301d0ecf6b1f6b4b71c541c2746d2a0a131847b6 Mon Sep 17 00:00:00 2001 From: TranceLove Date: Thu, 10 Sep 2020 13:17:38 +0800 Subject: [PATCH 10/10] Add test case of move file from SSH to device verification --- .../asynctasks/MoveFilesTest.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFilesTest.java diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFilesTest.java b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFilesTest.java new file mode 100644 index 000000000..c2a7a1459 --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/MoveFilesTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.asynchronous.asynctasks; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Paths; +import java.security.SecureRandom; +import java.util.ArrayList; + +import org.apache.commons.compress.utils.IOUtils; +import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import com.amaze.filemanager.filesystem.HybridFileParcelable; +import com.amaze.filemanager.filesystem.ssh.AbstractSftpServerTest; +import com.amaze.filemanager.shadows.ShadowMultiDex; +import com.amaze.filemanager.utils.OpenMode; + +import android.os.Environment; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +@Config(shadows = {ShadowMultiDex.class}) +public class MoveFilesTest extends AbstractSftpServerTest { + + private File sshServerRoot; + + @Before + @Override + public void setUp() throws IOException { + sshServerRoot = Environment.getExternalStoragePublicDirectory("sshserver-test"); + serverPort = + createSshServer( + new VirtualFileSystemFactory(Paths.get(sshServerRoot.getAbsolutePath())), 64000); + prepareSshConnection(); + } + + @Test + @Ignore("Not yet for CI testing until pass") + public void testMoveFilesUsingSftp() throws Exception { + File sourceFile = new File(sshServerRoot, "testfile.bin"); + SecureRandom rnd = new SecureRandom(); + byte[] randomBytes = new byte[32]; + rnd.nextBytes(randomBytes); + IOUtils.copy(new ByteArrayInputStream(randomBytes), new FileOutputStream(sourceFile)); + + ArrayList filesToCopy = new ArrayList<>(); + HybridFileParcelable file = + new HybridFileParcelable( + "ssh://testuser:testpassword@127.0.0.1:" + serverPort + "/testfile.bin"); + file.generateMode(ApplicationProvider.getApplicationContext()); + filesToCopy.add(file); + ArrayList> filesToCopyPerFolder = new ArrayList<>(); + filesToCopyPerFolder.add(filesToCopy); + MoveFiles task = + new MoveFiles( + filesToCopyPerFolder, null, ApplicationProvider.getApplicationContext(), OpenMode.FILE); + + ArrayList paths = new ArrayList<>(); + paths.add(Environment.getExternalStorageDirectory().getAbsolutePath()); + task.doInBackground(paths); + + assertFalse(sourceFile.exists()); + assertTrue(new File(Environment.getExternalStorageDirectory(), "testfile.bin").exists()); + } +}