Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Dynamic Config Provider #82

Merged
merged 42 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b5162a3
Start JSON-COnfigurations
newhinton Feb 24, 2023
cca10d1
start implementing dynamic ui
newhinton Mar 24, 2023
d53842f
switch to 'rclone config providers'
newhinton Apr 3, 2023
fb05f4c
build dynamic ui from provider
newhinton Apr 3, 2023
d0e8c0e
build dynamic ui from provider
newhinton Apr 26, 2023
4747bc0
fix margins
newhinton Apr 26, 2023
9bd10e4
update constraints
newhinton Apr 26, 2023
104446d
Simplify Remotes List
newhinton Apr 27, 2023
cc33a9e
remove translations to fix offset-issues in german #86
newhinton Apr 27, 2023
00066e6
Migrate Setup to DynamicConfig for majority of remotes
newhinton Apr 27, 2023
7b95734
Remove HUBIC due to deprecation and shutdown
newhinton May 19, 2023
3083c6c
remove ambiguity from Rclone.java
newhinton May 19, 2023
3243160
Remove OAuth-Configs
newhinton May 19, 2023
a06e598
Rename .java to .kt
newhinton May 19, 2023
153a4d5
Make the provider-list dynamic too
newhinton May 19, 2023
d9fbf95
Add special capitalization
newhinton May 19, 2023
41b1c77
create checkbox view
newhinton May 19, 2023
0dea848
remove layouts
newhinton May 19, 2023
4aa4faa
use proper case for options
newhinton May 19, 2023
2b4dc49
properly close fragment (with deprecated function. Good Luck!)
newhinton May 19, 2023
d05df58
Hide advanced options behind toggle
newhinton May 28, 2023
459e933
Merge branch 'master' into feature/noid/configjson
newhinton May 29, 2023
3f53432
remove xml after merge
newhinton May 29, 2023
2d3a7ac
fix theme on dynamic configurator
newhinton May 29, 2023
b5daa68
use mYou-theme on dynamic configurator
newhinton May 29, 2023
d5e560e
remove logging
newhinton May 29, 2023
3a8f9e0
allow editing of remotes
newhinton Jun 20, 2023
cdaf31c
dont input passwords from options since they are obfuscated
newhinton Jun 20, 2023
9d778bf
Merge branch 'master' into feature/noid/configjson
newhinton Aug 9, 2023
b09a67c
check if keys are available before using them
newhinton Aug 9, 2023
b606998
implement int and sizesuffix
newhinton Aug 19, 2023
3c9aadb
rclone: cache provider on disk to not start an rclone process
newhinton Aug 19, 2023
0e51c05
remote config list - use proper recycler view to circumvent slow list…
newhinton Aug 19, 2023
56cbb94
fix back button behaviour
newhinton Aug 20, 2023
1aa1d0e
Rename .java to .kt
newhinton Aug 20, 2023
a254ce6
refactor RemoteConfig to kotlin
newhinton Aug 20, 2023
b395639
improve navigation flow
newhinton Aug 20, 2023
316d2c0
Merge remote-tracking branch 'origin/master' into feature/noid/config…
newhinton Aug 20, 2023
9950124
add baseline from current master(b8b169a)
newhinton Aug 22, 2023
a3def46
fix new lint issues
newhinton Aug 22, 2023
65c36ee
Make whole row clickable
newhinton Aug 22, 2023
297f42b
finalize suffix dropdown
newhinton Aug 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ android {
useLegacyPackaging true
}
}

lint {
baseline = file("lint-baseline.xml")
}
namespace 'ca.pkay.rcloneexplorer'


Expand Down
4,325 changes: 4,325 additions & 0 deletions app/lint-baseline.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package ca.pkay.rcloneexplorer.Fragments;

import static ca.pkay.rcloneexplorer.RemoteConfig.RemoteConfig.CONFIG_EDIT_CODE;
import static ca.pkay.rcloneexplorer.RemoteConfig.RemoteConfig.CONFIG_EDIT_TARGET;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
Expand Down Expand Up @@ -51,6 +54,7 @@ public class RemotesFragment extends Fragment implements RemotesRecyclerViewAdap

private final int CONFIG_REQ_CODE = 171;
private final int CONFIG_RECREATE_REQ_CODE = 156;

private Rclone rclone;
private RemotesRecyclerViewAdapter recyclerViewAdapter;
private List<RemoteItem> remotes;
Expand Down Expand Up @@ -270,36 +274,36 @@ private void showRemoteMenu(View view, final RemoteItem remoteItem) {
PopupMenu popupMenu = new PopupMenu(context, view);
popupMenu.getMenuInflater().inflate(R.menu.remote_options, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(item -> {
switch (item.getItemId()) {
case R.id.action_remote_properties:
showRemotePropertiesDialog(remoteItem);
break;
case R.id.action_delete:
deleteRemote(remoteItem);
break;
case R.id.action_remote_rename:
renameRemote(remoteItem);
break;
case R.id.action_pin:
if (remoteItem.isPinned()) {
unPinRemote(remoteItem);
} else {
pinRemote(remoteItem);
}
break;
case R.id.action_favorite:
if (remoteItem.isDrawerPinned()) {
unpinFromDrawer(remoteItem);
} else {
pinToDrawer(remoteItem);
}
break;
case R.id.action_add_to_home_screen:
AppShortcutsHelper.addRemoteToHomeScreen(context, remoteItem);
break;
default:
return false;
int itemID = item.getItemId();

if(itemID == R.id.action_remote_properties) {
showRemotePropertiesDialog(remoteItem);
} else if (itemID == R.id.action_edit_remote) {
Intent intent = new Intent(context, RemoteConfig.class);
intent.putExtra(CONFIG_EDIT_TARGET, remoteItem.getName());
startActivityForResult(intent, CONFIG_EDIT_CODE);
} else if (itemID == R.id.action_delete) {
deleteRemote(remoteItem);
} else if (itemID == R.id.action_remote_rename) {
renameRemote(remoteItem);
} else if (itemID == R.id.action_pin) {
if (remoteItem.isPinned()) {
unPinRemote(remoteItem);
} else {
pinRemote(remoteItem);
}
} else if (itemID == R.id.action_favorite) {
if (remoteItem.isDrawerPinned()) {
unpinFromDrawer(remoteItem);
} else {
pinToDrawer(remoteItem);
}
} else if (itemID == R.id.action_add_to_home_screen) {
AppShortcutsHelper.addRemoteToHomeScreen(context, remoteItem);
} else {
return false;
}

return true;
});
popupMenu.show();
Expand Down
13 changes: 4 additions & 9 deletions app/src/main/java/ca/pkay/rcloneexplorer/Items/RemoteItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import ca.pkay.rcloneexplorer.R;
import io.github.x0b.safdav.file.SafConstants;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import ca.pkay.rcloneexplorer.R;
import io.github.x0b.safdav.file.SafConstants;

public class RemoteItem implements Comparable<RemoteItem>, Parcelable {

/**
Expand All @@ -40,7 +42,6 @@ public class RemoteItem implements Comparable<RemoteItem>, Parcelable {
public static final int GOOGLE_CLOUD_STORAGE = 12;
public static final int GOOGLE_DRIVE = 13;
public static final int GOOGLE_PHOTOS = 14;
public static final int HUBIC = 15;
public static final int JOTTACLOUD = 16;
public static final int KOOFR = 17;
public static final int LOCAL = 18;
Expand Down Expand Up @@ -124,7 +125,6 @@ public boolean isDirectoryModifiedTimeSupported() {
switch (type) {
case DROPBOX:
case B2:
case HUBIC:
case GOOGLE_PHOTOS:
return false;
default:
Expand All @@ -134,7 +134,6 @@ public boolean isDirectoryModifiedTimeSupported() {

public boolean isOAuth() {
switch (type) {
case HUBIC:
case PCLOUD:
case PREMIUMIZEME:
case BOX:
Expand Down Expand Up @@ -311,8 +310,6 @@ private int getTypeFromString(String type) {
return HTTP;
case "swift":
return SWIFT;
case "hubic":
return HUBIC;
case "jottacloud":
return JOTTACLOUD;
case "koofr":
Expand Down Expand Up @@ -377,8 +374,6 @@ public int getRemoteIcon(int type) {
return R.drawable.ic_google;
case RemoteItem.GOOGLE_PHOTOS:
return R.drawable.ic_google_photos;
case RemoteItem.HUBIC:
return R.drawable.ic_hubic_black;
case RemoteItem.KOOFR:
return R.drawable.ic_koofr;
case RemoteItem.MEGA:
Expand Down
131 changes: 131 additions & 0 deletions app/src/main/java/ca/pkay/rcloneexplorer/Rclone.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand All @@ -32,6 +34,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
Expand All @@ -45,6 +48,7 @@
import ca.pkay.rcloneexplorer.Items.FileItem;
import ca.pkay.rcloneexplorer.Items.RemoteItem;
import ca.pkay.rcloneexplorer.Items.SyncDirectionObject;
import ca.pkay.rcloneexplorer.rclone.Provider;
import ca.pkay.rcloneexplorer.util.FLog;
import es.dmoral.toasty.Toasty;
import io.github.x0b.safdav.SafAccessProvider;
Expand All @@ -60,6 +64,8 @@ public class Rclone {
public static final int SERVE_PROTOCOL_WEBDAV = 2;
public static final int SERVE_PROTOCOL_FTP = 3;
public static final int SERVE_PROTOCOL_DLNA = 4;

public static final String RCLONE_CONFIG_NAME_KEY = "rclone_remote_name";
private static volatile Boolean isCompatible;
private static SafDAVServer safDAVServer;
private Context context;
Expand Down Expand Up @@ -487,6 +493,47 @@ public Process configCreate(List<String> options) {
}
}

@Nullable
public HashMap<String, String> getConfig(String name) {
String[] command = createCommand("config", "dump");
StringBuilder output = new StringBuilder();
Process process;
JSONObject configs = new JSONObject();

HashMap<String, String> options = new HashMap<>();

try {
process = getRuntimeProcess(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
}

process.waitFor();
if (process.exitValue() != 0) {
Toasty.error(context, context.getString(R.string.error_getting_config), Toast.LENGTH_SHORT, true).show();
logErrorOutput(process);
}

configs = new JSONObject(output.toString());
} catch (IOException | InterruptedException | JSONException e) {
FLog.e(TAG, "getRemotes: error retrieving remotes", e);
}

JSONObject selectedConfig = configs.optJSONObject(name);
Iterator<String> keys = selectedConfig.keys();

while(keys.hasNext()) {
String key = keys.next();
options.put(key, selectedConfig.optString(key));
}

options.put(RCLONE_CONFIG_NAME_KEY, name);
return options;

}

public Process configInteractive() throws IOException {
String[] command = createCommand("config");
String[] environment = getRcloneEnv();
Expand Down Expand Up @@ -1437,4 +1484,88 @@ public static String getLocalRemotePathPrefix(RemoteItem item, Context context)
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
}

public ArrayList<Provider> getProviders() throws JSONException {
return getProviders(false);
}
public ArrayList<Provider> getProviders(boolean silent) throws JSONException {

JSONArray remotesJSON;
int versionCode = BuildConfig.VERSION_CODE;
File file = new File(context.getCacheDir(), "rclone.provider."+versionCode);

if(!file.exists()) {
String[] command = createCommand("config", "providers");
StringBuilder output = new StringBuilder();
Process process;

try {
process = getRuntimeProcess(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
}

process.waitFor();
if (process.exitValue() != 0) {
if(!silent){
Toasty.error(context, context.getString(R.string.error_getting_remotes), Toast.LENGTH_SHORT, true).show();
}
logErrorOutput(process);
return new ArrayList<>();
}

remotesJSON = new JSONArray(output.toString());
} catch (IOException | InterruptedException | JSONException e) {
FLog.e(TAG, "getRemotes: error retrieving remotes", e);
return new ArrayList<>();
}

try {
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(remotesJSON.toString(4));
bw.close();
} catch (IOException e) {
Toasty.error(context, context.getString(R.string.error_getting_remotes), Toast.LENGTH_SHORT, true).show();
FLog.e(TAG, "Could not save providers to cache!", e);
return new ArrayList<>();
}
} else {
StringBuilder fileContent = new StringBuilder();
try {
FileInputStream inputstream = new FileInputStream(file);
byte[] buffer = new byte[8128];
int size;
while ((size = inputstream.read(buffer)) != -1) {
fileContent.append(new String(buffer, 0, size));
}
} catch (IOException e) {
Toasty.error(context, context.getString(R.string.error_getting_remotes), Toast.LENGTH_SHORT, true).show();
FLog.e(TAG, "Could not read cached providers, but the file exists! Please clear your app cache.", e);
return new ArrayList<>();
}

remotesJSON = new JSONArray(fileContent.toString());
}

ArrayList<Provider> providerItems = new ArrayList<>();

for (int i = 0; i < remotesJSON.length(); i++) {
providerItems.add(Provider.Companion.newInstance(remotesJSON.getJSONObject(i)));
}

return providerItems;
}

public Provider getProvider(String name) throws JSONException {
for (Provider provider : getProviders()) {
if(provider.getName().equals(name)){
return provider;
}
}
return null;
}

}
Loading