mirror of
https://github.com/TeamAmaze/AmazeFileManager.git
synced 2026-10-18 05:37:24 +08:00
Merge pull request #1977 from TeamAmaze/bugfix/1942
This commit is contained in:
@@ -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')
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Context> 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 <Params, Result> void runInParallel(
|
||||
final CustomAsyncCallbacks<Params, Result> customAsyncCallbacks) {
|
||||
|
||||
synchronized (customAsyncCallbacks) {
|
||||
new AsyncTask<Params, Void, Result>() {
|
||||
@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<Params, Result> {
|
||||
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() {
|
||||
|
||||
@@ -194,27 +194,28 @@ public class MoveFiles extends AsyncTask<ArrayList<String>, 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 {
|
||||
|
||||
|
||||
@@ -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<CloudEntry> getAllEntries() throws CloudPluginException {
|
||||
|
||||
if (!CloudSheetFragment.isCloudProviderAvailable(context)) throw new CloudPluginException();
|
||||
|
||||
return Arrays.asList(database.cloudEntryDao().list());
|
||||
return database.cloudEntryDao().list().subscribeOn(Schedulers.io()).blockingGet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<EncryptedEntry> encryptedEntryList =
|
||||
database.encryptedEntryDao().list().subscribeOn(Schedulers.io()).blockingGet();
|
||||
EncryptedEntry[] encryptedEntries = new EncryptedEntry[encryptedEntryList.size()];
|
||||
return encryptedEntryList.toArray(encryptedEntries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Tab> tabList = database.tabDao().list().subscribeOn(Schedulers.io()).blockingGet();
|
||||
Tab[] tabs = new Tab[tabList.size()];
|
||||
return tabList.toArray(tabs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> getHistoryLinkedList() {
|
||||
LinkedList<String> 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<VoidValue> 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<String> getListViewList() {
|
||||
return new ArrayList<>(Arrays.asList(utilitiesDatabase.listEntryDao().listPaths()));
|
||||
return new ArrayList<>(
|
||||
utilitiesDatabase.listEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet());
|
||||
}
|
||||
|
||||
public ArrayList<String> getGridViewList() {
|
||||
return new ArrayList<>(Arrays.asList(utilitiesDatabase.gridEntryDao().listPaths()));
|
||||
return new ArrayList<>(
|
||||
utilitiesDatabase.gridEntryDao().listPaths().subscribeOn(Schedulers.io()).blockingGet());
|
||||
}
|
||||
|
||||
public ArrayList<String[]> getBookmarksList() {
|
||||
|
||||
ArrayList<String[]> 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<String[]> getSmbList() {
|
||||
ArrayList<String[]> retval = new ArrayList<String[]>();
|
||||
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<String[]> getSftpList() {
|
||||
ArrayList<String[]> retval = new ArrayList<String[]>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Bookmark>> list();
|
||||
|
||||
@Query(
|
||||
"SELECT * FROM "
|
||||
@@ -59,7 +64,7 @@ public interface BookmarkEntryDao {
|
||||
+ " = :name AND "
|
||||
+ COLUMN_PATH
|
||||
+ " = :path")
|
||||
public Bookmark findByNameAndPath(String name, String path);
|
||||
Single<Bookmark> 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);
|
||||
}
|
||||
|
||||
@@ -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<CloudEntry> findByServiceType(int serviceType);
|
||||
|
||||
@Query("SELECT * FROM " + TABLE_CLOUD_PERSIST)
|
||||
public CloudEntry[] list();
|
||||
Single<List<CloudEntry>> list();
|
||||
|
||||
@Update
|
||||
public void update(CloudEntry entry);
|
||||
Completable update(CloudEntry entry);
|
||||
|
||||
@Delete
|
||||
public void delete(CloudEntry entry);
|
||||
Completable delete(CloudEntry entry);
|
||||
}
|
||||
|
||||
@@ -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<EncryptedEntry> 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<EncryptedEntry>> list();
|
||||
}
|
||||
|
||||
@@ -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<List<String>> listPaths();
|
||||
|
||||
@Query("DELETE FROM " + TABLE_GRID + " WHERE " + COLUMN_PATH + " = :path")
|
||||
public void deleteByPath(String path);
|
||||
Completable deleteByPath(String path);
|
||||
}
|
||||
|
||||
@@ -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<List<String>> listPaths();
|
||||
|
||||
@Query("DELETE FROM " + TABLE_HIDDEN + " WHERE " + COLUMN_PATH + " = :path")
|
||||
public void deleteByPath(String path);
|
||||
Completable deleteByPath(String path);
|
||||
}
|
||||
|
||||
@@ -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<History>> 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();
|
||||
}
|
||||
|
||||
@@ -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<java.util.List<String>> listPaths();
|
||||
|
||||
@Query("DELETE FROM " + TABLE_LIST + " WHERE " + COLUMN_PATH + " = :path")
|
||||
public void deleteByPath(String path);
|
||||
Completable deleteByPath(String path);
|
||||
}
|
||||
|
||||
@@ -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<SftpEntry>> list();
|
||||
|
||||
@Query(
|
||||
"SELECT * FROM "
|
||||
@@ -62,22 +67,22 @@ public interface SftpEntryDao {
|
||||
+ " = :name AND "
|
||||
+ COLUMN_PATH
|
||||
+ " = :path")
|
||||
public SftpEntry findByNameAndPath(String name, String path);
|
||||
Single<SftpEntry> findByNameAndPath(String name, String path);
|
||||
|
||||
@Query("SELECT * FROM " + TABLE_SFTP + " WHERE " + COLUMN_NAME + " = :name")
|
||||
public SftpEntry findByName(String name);
|
||||
Single<SftpEntry> findByName(String name);
|
||||
|
||||
@Query("SELECT " + COLUMN_HOST_PUBKEY + " FROM " + TABLE_SFTP + " WHERE PATH = :uri")
|
||||
public String getSshHostKey(String uri);
|
||||
Single<String> getSshHostKey(String uri);
|
||||
|
||||
@Query("SELECT " + COLUMN_PRIVATE_KEY_NAME + " FROM " + TABLE_SFTP + " WHERE PATH = :uri")
|
||||
public String getSshAuthPrivateKeyName(String uri);
|
||||
Single<String> getSshAuthPrivateKeyName(String uri);
|
||||
|
||||
@Query("SELECT " + COLUMN_PRIVATE_KEY + " FROM " + TABLE_SFTP + " WHERE PATH = :uri")
|
||||
public String getSshAuthPrivateKey(String uri);
|
||||
Single<String> 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);
|
||||
}
|
||||
|
||||
@@ -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<SmbEntry>> list();
|
||||
|
||||
@Query(
|
||||
"SELECT * FROM "
|
||||
@@ -59,10 +64,10 @@ public interface SmbEntryDao {
|
||||
+ " = :name AND "
|
||||
+ COLUMN_PATH
|
||||
+ " = :path")
|
||||
public SmbEntry findByNameAndPath(String name, String path);
|
||||
Single<SmbEntry> 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);
|
||||
}
|
||||
|
||||
@@ -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<Sort> 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);
|
||||
}
|
||||
|
||||
@@ -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<Tab> find(int tabNo);
|
||||
|
||||
@Query("SELECT * FROM " + TABLE_TAB)
|
||||
public Tab[] list();
|
||||
Single<List<Tab>> list();
|
||||
}
|
||||
|
||||
@@ -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<Void, List<String>>(null) {
|
||||
Maybe.create(
|
||||
(MaybeOnSubscribe<List<String>>)
|
||||
emitter -> {
|
||||
List<String> retval = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public List<String> doInBackground() {
|
||||
for (Uri uri : uris) {
|
||||
|
||||
List<String> 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<List<String>>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> 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<String> result) {
|
||||
if (result != null) {
|
||||
List<String> paths = (List<String>) 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() {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<HybridFileParcelable> 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<Spanned>() {
|
||||
@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<HybridFileParcelable> 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() {
|
||||
|
||||
@@ -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> 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<T>) () -> 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;
|
||||
|
||||
@@ -58,12 +58,14 @@ public class SshConnectionPool {
|
||||
|
||||
private static final String TAG = SshConnectionPool.class.getSimpleName();
|
||||
|
||||
private static SshConnectionPool instance = null;
|
||||
|
||||
private final Map<String, SSHClient> connections;
|
||||
|
||||
private SshConnectionPool() {
|
||||
connections = new ConcurrentHashMap<String, SSHClient>();
|
||||
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
|
||||
|
||||
@@ -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<Void, Void>(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<String[]> 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<String[]> servers = new ArrayList<String[]>();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+8
-7
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ public class DataUtils {
|
||||
public void clearHistory() {
|
||||
history.clear();
|
||||
if (dataChangeListener != null) {
|
||||
AppConfig.runInBackground(() -> dataChangeListener.onHistoryCleared());
|
||||
AppConfig.getInstance().runInBackground(() -> dataChangeListener.onHistoryCleared());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2020 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>,
|
||||
* Emmanuel Messulam<emmanuelbendavid@gmail.com>, Raymond Lai <airwave209gt at gmail.com> 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<HybridFileParcelable> 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<ArrayList<HybridFileParcelable>> filesToCopyPerFolder = new ArrayList<>();
|
||||
filesToCopyPerFolder.add(filesToCopy);
|
||||
MoveFiles task =
|
||||
new MoveFiles(
|
||||
filesToCopyPerFolder, null, ApplicationProvider.getApplicationContext(), OpenMode.FILE);
|
||||
|
||||
ArrayList<String> paths = new ArrayList<>();
|
||||
paths.add(Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
task.doInBackground(paths);
|
||||
|
||||
assertFalse(sourceFile.exists());
|
||||
assertTrue(new File(Environment.getExternalStorageDirectory(), "testfile.bin").exists());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This method throws {@link AssertionError} directly so test code don't need to handle
|
||||
* possible exceptions occur when accessing
|
||||
*
|
||||
* <pre>AppConfig.backgroundHandler</pre>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user