diff --git a/app/build.gradle b/app/build.gradle index af9060614..a5079eca4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -114,6 +114,8 @@ 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" annotationProcessor 'androidx.annotation:annotation:1.1.0' //For tests @@ -218,6 +220,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 2ef2746d1..0579fe5a8 100644 --- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java +++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java @@ -36,9 +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.HandlerThread; import android.os.StrictMode; import android.widget.Toast; @@ -47,6 +44,11 @@ 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 { public static final String TAG = AppConfig.class.getSimpleName(); @@ -56,9 +58,6 @@ public class AppConfig extends GlideApplication { private ImageLoader imageLoader; private UtilsHandler utilsHandler; - private static Handler applicationhandler = new Handler(); - private HandlerThread backgroundHandlerThread; - private static Handler backgroundHandler; private WeakReference mainActivityContext; private static ScreenUtils screenUtils; @@ -77,7 +76,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,10 +85,7 @@ public class AppConfig extends GlideApplication { utilsProvider = new UtilitiesProvider(this); utilsHandler = new UtilsHandler(this, utilitiesDatabase); - backgroundHandlerThread.start(); - backgroundHandler = new Handler(backgroundHandlerThread.getLooper()); - - runInBackground(jcifs.Config::registerSmbURLHandler); + runInBackground(Config::registerSmbURLHandler); // disabling file exposure method check for api n+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); @@ -100,7 +95,6 @@ public class AppConfig extends GlideApplication { @Override public void onTerminate() { super.onTerminate(); - backgroundHandlerThread.quit(); } /** @@ -108,54 +102,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); - } - } - - /** - * 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() {} + public void runInBackground(Runnable runnable) { + Completable.fromRunnable(runnable).subscribeOn(Schedulers.io()).subscribe(); } /** @@ -212,7 +160,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()).subscribe(); } public static synchronized AppConfig getInstance() { 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/CloudHandler.java b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java index 3c5d6e40e..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,12 @@ 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.schedulers.Schedulers; + /** Created by vishal on 18/4/17. */ public class CloudHandler { @@ -57,13 +59,21 @@ 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())); + .findByServiceType(serviceType.ordinal()) + .subscribeOn(Schedulers.io()) + .subscribe( + cloudEntry -> + database + .cloudEntryDao() + .delete(cloudEntry) + .subscribeOn(Schedulers.io()) + .subscribe()); } public void updateEntry(OpenMode serviceType, CloudEntry newCloudEntry) @@ -71,20 +81,29 @@ 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()); + 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 Arrays.asList(database.cloudEntryDao().list()); + 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 060ce7007..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,11 +20,17 @@ package com.amaze.filemanager.database; +import java.util.List; + import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.models.explorer.EncryptedEntry; +import android.util.Log; + import androidx.annotation.NonNull; +import io.reactivex.schedulers.Schedulers; + /** Created by vishal on 15/4/17. */ public class CryptHandler { @@ -44,22 +50,31 @@ public class CryptHandler { } public void addEntry(EncryptedEntry encryptedEntry) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().insert(encryptedEntry)); + database.encryptedEntryDao().insert(encryptedEntry).subscribeOn(Schedulers.io()).subscribe(); } public void clear(String path) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().delete(path)); + database.encryptedEntryDao().delete(path).subscribeOn(Schedulers.io()).subscribe(); } public void updateEntry(EncryptedEntry oldEncryptedEntry, EncryptedEntry newEncryptedEntry) { - AppConfig.runInBackground(() -> database.encryptedEntryDao().update(newEncryptedEntry)); + database.encryptedEntryDao().update(newEncryptedEntry).subscribeOn(Schedulers.io()).subscribe(); } public EncryptedEntry findEntry(String path) { - return database.encryptedEntryDao().select(path); + 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() { - return database.encryptedEntryDao().list(); + 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 0d690a202..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,10 +31,13 @@ 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. */ public class SortHandler { @@ -70,19 +73,25 @@ public class SortHandler { } public void addEntry(Sort sort) { - AppConfig.runInBackground(() -> database.sortDao().insert(sort)); + database.sortDao().insert(sort).subscribeOn(Schedulers.io()).subscribe(); } public void clear(String path) { - AppConfig.runInBackground(() -> database.sortDao().clear(path)); + database.sortDao().clear(path).subscribeOn(Schedulers.io()).subscribe(); } public void updateEntry(Sort oldSort, Sort newSort) { - AppConfig.runInBackground(() -> database.sortDao().update(newSort)); + database.sortDao().update(newSort).subscribeOn(Schedulers.io()).subscribe(); } @Nullable public Sort findEntry(String path) { - return database.sortDao().find(path); + 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 93eef767f..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,12 +20,18 @@ 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; + /** Created by Vishal on 9/17/2014. */ public class TabHandler { @@ -45,19 +51,27 @@ 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); + 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() { - return database.tabDao().list(); + 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 cbfa7b352..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; @@ -50,6 +49,8 @@ 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 * utilities like history, hidden files, list paths, grid paths, bookmarks, SMB entry @@ -80,80 +81,109 @@ 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!"); - } - }); + 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.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() { @@ -181,23 +211,33 @@ public class UtilsHandler { String sshKeyName, String sshKey) { - SftpEntry entry = utilitiesDatabase.sftpEntryDao().findByName(oldConnectionName); + 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; - } - - AppConfig.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().subscribeOn(Schedulers.io()).blockingGet()) { paths.add(history.path); } return paths; @@ -207,24 +247,28 @@ public class UtilsHandler { ConcurrentRadixTree paths = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory()); - for (String path : utilitiesDatabase.hiddenEntryDao().listPaths()) { + for (String path : + utilitiesDatabase.hiddenEntryDao().listPaths().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().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getGridViewList() { - return new ArrayList<>(Arrays.asList(utilitiesDatabase.gridEntryDao().listPaths())); + return new ArrayList<>( + utilitiesDatabase.gridEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet()); } public ArrayList getBookmarksList() { ArrayList row = new ArrayList<>(); - for (Bookmark bookmark : utilitiesDatabase.bookmarkEntryDao().list()) { + for (Bookmark bookmark : + utilitiesDatabase.bookmarkEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { row.add(new String[] {bookmark.name, bookmark.path}); } return row; @@ -232,7 +276,8 @@ public class UtilsHandler { public ArrayList getSmbList() { ArrayList retval = new ArrayList(); - for (SmbEntry entry : utilitiesDatabase.smbEntryDao().list()) { + for (SmbEntry entry : + utilitiesDatabase.smbEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { try { String path = SmbUtil.getSmbDecryptedPath(context, entry.path); @@ -253,7 +298,8 @@ public class UtilsHandler { public List getSftpList() { ArrayList retval = new ArrayList(); - for (SftpEntry entry : utilitiesDatabase.sftpEntryDao().list()) { + for (SftpEntry entry : + utilitiesDatabase.sftpEntryDao().list().subscribeOn(Schedulers.io()).blockingGet()) { String path = SshClientUtils.decryptSshPathAsNecessary(entry.path); if (path == null) { @@ -272,22 +318,56 @@ public class UtilsHandler { public String getSshHostKey(String uri) { uri = SshClientUtils.encryptSshPathAsNecessary(uri); if (uri != null) { - return utilitiesDatabase.sftpEntryDao().getSshHostKey(uri); + 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); + 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); + 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); + utilitiesDatabase + .bookmarkEntryDao() + .deleteByNameAndPath(name, path) + .subscribeOn(Schedulers.io()) + .subscribe(); } /** @@ -297,21 +377,45 @@ 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; + 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; - utilitiesDatabase.bookmarkEntryDao().update(bookmark); + utilitiesDatabase.bookmarkEntryDao().update(bookmark).subscribeOn(Schedulers.io()).subscribe(); } public void renameSMB(String oldName, String oldPath, String newName, String newPath) { @@ -321,24 +425,31 @@ 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); - smbEntry.name = newName; - smbEntry.path = newPath; - - utilitiesDatabase.smbEntryDao().update(smbEntry); + utilitiesDatabase + .smbEntryDao() + .update(smbEntry) + .subscribeOn(Schedulers.io()) + .subscribe(); + }); } public void clearTable(Operation table) { - AppConfig.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..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,6 +24,8 @@ 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; @@ -31,6 +33,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link Bookmark}. Concrete class is generated by Room during * build. @@ -43,13 +48,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(); + Single> list(); @Query( "SELECT * FROM " @@ -59,7 +64,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 +74,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..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; @@ -31,6 +33,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link CloudEntry}. Concrete class is generated by Room * during build. @@ -43,18 +48,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(); + Single> 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..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; @@ -31,6 +33,9 @@ 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 EncryptedEntry}. Concrete class is generated by Room * during build. @@ -43,18 +48,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(); + 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 392b528fb..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,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_GRID; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.Grid; import androidx.room.Dao; @@ -30,6 +32,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link Grid}. Concrete class is generated by Room during * build. @@ -42,14 +47,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(); + Single> 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..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,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_HIDDEN; +import java.util.List; + import com.amaze.filemanager.database.models.utilities.Hidden; import androidx.room.Dao; @@ -30,6 +32,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link Hidden}. Concrete class is generated by Room during * build. @@ -42,14 +47,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(); + Single> 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..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; @@ -31,6 +33,9 @@ import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link History}. Concrete class is generated by Room during * build. @@ -43,17 +48,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(); + Single> 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..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 @@ -31,6 +31,9 @@ import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link List}. Concrete class is generated by Room during * build. @@ -43,14 +46,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(); + Single> 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..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,6 +27,8 @@ 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; @@ -34,6 +36,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link SftpEntry}. Concrete class is generated by Room * during build. @@ -46,13 +51,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(); + Single> list(); @Query( "SELECT * FROM " @@ -62,22 +67,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 +92,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..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,6 +24,8 @@ 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; @@ -31,6 +33,9 @@ import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link SmbEntry}. Concrete class is generated by Room during * build. @@ -43,13 +48,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(); + Single> list(); @Query( "SELECT * FROM " @@ -59,10 +64,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 +77,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..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 @@ -31,6 +31,9 @@ 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 * build. @@ -43,15 +46,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..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; @@ -31,6 +33,9 @@ import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Transaction; +import io.reactivex.Completable; +import io.reactivex.Single; + /** * {@link Dao} interface definition for {@link Tab}. Concrete class is generated by Room during * build. @@ -42,15 +47,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(); + 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 0bd3be912..bad314ebf 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,12 @@ import android.widget.Toast; 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; @@ -200,197 +206,214 @@ 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(HybridFile.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(); + } + } + } + if (retval.size() > 0) { + emitter.onSuccess(retval); + } else { + emitter.onError(new Exception()); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + new MaybeObserver>() { + @Override + public void onSubscribe(Disposable d) {} + + @Override + public void onSuccess(List paths) { + 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(); + @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/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 556c2b212..82b367e8b 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -60,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 +440,6 @@ public class HybridFile { } public boolean isDirectory(Context context) { - boolean isDirectory; switch (mode) { case SFTP: @@ -452,19 +453,24 @@ 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 (SmbException e) { + isDirectory = + Single.fromCallable(() -> new SmbFile(path).isDirectory()) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception 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/PasteHelper.java b/app/src/main/java/com/amaze/filemanager/filesystem/PasteHelper.java index 5b25001d4..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,13 @@ 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; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Special immutable class for handling cut/copy operations. @@ -129,26 +136,47 @@ 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, - mainActivity, - mainActivity.isRootExplorer()) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arrayList); - dismissSnackbar(true); + Single.fromCallable(() -> getSnackbarContent()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + 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) { + Log.e( + getClass().getSimpleName(), + "Failed to show paste snackbar due to " + e.getCause()); + e.printStackTrace(); + } }); - 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 b8f3d8c47..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; @@ -47,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; @@ -55,7 +58,8 @@ import net.schmizz.sshj.sftp.RemoteResourceInfo; 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. @@ -68,17 +72,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 = + Single.fromCallable((Callable) () -> template.execute(client)) + .subscribeOn(Schedulers.io()) + .blockingGet(); + } catch (Exception e) { + Log.e(TAG, "Error executing template method", e); + } finally { + if (template.closeClientOnFinish) { + tryDisconnect(client); + } } } 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..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 @@ -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,16 +172,17 @@ public class SshConnectionPool { * @see MainActivity#onDestroy() * @see MainActivity#exit() */ - public void expungeAllConnections() { - AppConfig.runInBackground( - () -> { - if (!connections.isEmpty()) { - for (SSHClient connection : connections.values()) { - SshClientUtils.tryDisconnect(connection); - } - connections.clear(); - } - }); + public void shutdown() { + 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 b3e75be0c..ac38bc615 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,11 @@ import androidx.loader.content.CursorLoader; 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 implements SmbConnectionListener, @@ -305,7 +310,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"); @@ -365,79 +370,87 @@ public class MainActivity extends PermissionsActivity checkForExternalPermission(); - AppConfig.runInParallel( - new AppConfig.CustomAsyncCallbacks(null) { - @Override - public Void doInBackground() { + Completable.fromRunnable( + () -> { + 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 CompletableObserver() { + @Override + public void onSubscribe(Disposable d) {} - 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); - 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.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); - } + @Override + public void onComplete() { + drawer.refreshDrawer(); + invalidateFragmentAndBundle(savedInstanceState); } - } 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"); - int selectedStorage = savedInstanceState.getInt(KEY_DRAWER_SELECTED, 0); - getDrawer().selectCorrectDrawerItem(selectedStorage); - } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + }); + } + + 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); + 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.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"); + int selectedStorage = savedInstanceState.getInt(KEY_DRAWER_SELECTED, 0); + getDrawer().selectCorrectDrawerItem(selectedStorage); + } } private void checkForExternalPermission() { @@ -603,10 +616,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() { @@ -864,7 +878,7 @@ public class MainActivity extends PermissionsActivity public void exit() { if (backPressedToExitOnce) { - SshConnectionPool.getInstance().expungeAllConnections(); + SshConnectionPool.getInstance().shutdown(); finish(); if (isRootExplorer()) { // TODO close all shells @@ -1122,12 +1136,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())); @@ -1135,12 +1150,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( @@ -1323,7 +1339,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 */ @@ -1836,10 +1852,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); @@ -1856,11 +1873,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/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/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/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()); + } +} 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 5d08b196d..59d1e5f09 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 @@ -72,7 +72,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); } 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 8761127ef..004087bef 100644 --- a/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java +++ b/app/src/test/java/com/amaze/filemanager/test/ShadowCryptUtilTest.java @@ -20,11 +20,14 @@ 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.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @@ -39,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"; @@ -71,9 +83,13 @@ public class ShadowCryptUtilTest { null, null)); - 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 2f011f86d..a2c8f025d 100644 --- a/app/src/test/java/com/amaze/filemanager/test/TestUtils.java +++ b/app/src/test/java/com/amaze/filemanager/test/TestUtils.java @@ -24,16 +24,11 @@ import static android.os.Build.VERSION_CODES.P; import static org.robolectric.Shadows.shadowOf; import java.io.File; -import java.lang.reflect.Field; 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; @@ -44,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#backgroundHandler - * @see AppConfig#runInBackground(Runnable) - */ - public static void flushAppConfigHandlerThread() { - try { - Field f = AppConfig.class.getDeclaredField("backgroundHandler"); - f.setAccessible(true); - Handler h = (Handler) f.get(null); - Scheduler scheduler = shadowOf(h.getLooper()).getScheduler(); - scheduler.advanceToLastPostedRunnable(); - } 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();