Add configurable parallel uploads and improve chunk upload settings

- Add parallel uploads toggle in settings (experimental, disabled by default)
- Increase mobile chunk size from 2MB to 4MB
- Configure parallel uploads: 3 chunks on WiFi, 2 on mobile (if enabled), 1 sequential (if disabled)
- Fix progress tracking when retrying missing chunks (subtract bytes to prevent >100%)
This commit is contained in:
2025-12-27 22:20:01 -05:00
parent 41363028aa
commit 04b3bea454
3 changed files with 103 additions and 16 deletions

View File

@@ -58,6 +58,9 @@ fun SettingsScreen(
var askBeforeUpload by remember {
mutableStateOf(PreferencesUtil.getAskBeforeUpload(context))
}
var enableParallelUploads by remember {
mutableStateOf(PreferencesUtil.isParallelUploadsEnabled(context))
}
var clientKeyVisible by remember { mutableStateOf(false) }
var serverAddressError by remember { mutableStateOf<String?>(null) }
@@ -176,6 +179,7 @@ fun SettingsScreen(
PreferencesUtil.setDefaultMaxDownloads(context, maxDl!!)
PreferencesUtil.setDefaultExpiryHours(context, expiry!!)
PreferencesUtil.setAskBeforeUpload(context, askBeforeUpload)
PreferencesUtil.setParallelUploadsEnabled(context, enableParallelUploads)
Toast.makeText(context, "Settings saved successfully!", Toast.LENGTH_SHORT).show()
onNavigateBack()
@@ -189,6 +193,7 @@ fun SettingsScreen(
defaultMaxDownloads = PreferencesUtil.getDefaultMaxDownloads(context).toString()
defaultExpiryHours = PreferencesUtil.getDefaultExpiryHours(context).toString()
askBeforeUpload = PreferencesUtil.getAskBeforeUpload(context)
enableParallelUploads = PreferencesUtil.isParallelUploadsEnabled(context)
// Clear errors
serverAddressError = null
@@ -381,6 +386,44 @@ fun SettingsScreen(
}
}
Spacer(modifier = Modifier.height(8.dp))
// Parallel Uploads Switch
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = "Upload Mode",
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (enableParallelUploads)
"Parallel: 2 chunks simultaneously (Experimental)"
else
"Sequential: One chunk at a time (Recommended)",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Switch(
checked = enableParallelUploads,
onCheckedChange = { enableParallelUploads = it }
)
}
}
Spacer(modifier = Modifier.height(16.dp))
// Action buttons

View File

@@ -48,12 +48,12 @@ public class ChunkedUploadManager {
// Chunk size configuration
private static final int WIFI_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB for WiFi
private static final int MOBILE_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB for mobile networks
private static final int MOBILE_CHUNK_SIZE = 4 * 1024 * 1024; // 4MB for mobile networks (increased from 2MB)
private static final int DEFAULT_CHUNK_SIZE = WIFI_CHUNK_SIZE;
// Max parallel uploads
private static final int WIFI_MAX_PARALLEL = 3;
private static final int MOBILE_MAX_PARALLEL = 1;
private static final int MOBILE_MAX_PARALLEL = 2; // 2 parallel chunks on mobile (testing with nginx config updates)
// Retry configuration
private static final int MAX_RETRY_ATTEMPTS = 3;
@@ -125,8 +125,14 @@ public class ChunkedUploadManager {
// Configure chunk size based on connection type
this.chunkSize = isWifiConnection ? WIFI_CHUNK_SIZE : MOBILE_CHUNK_SIZE;
// Configure max parallel uploads based on connection type
this.maxParallelUploads = isWifiConnection ? WIFI_MAX_PARALLEL : MOBILE_MAX_PARALLEL;
// Configure max parallel uploads based on connection type and user preference
boolean parallelEnabled = PreferencesUtil.isParallelUploadsEnabled(context);
if (isWifiConnection) {
this.maxParallelUploads = WIFI_MAX_PARALLEL;
} else {
// On mobile, use parallel uploads only if enabled in settings
this.maxParallelUploads = parallelEnabled ? MOBILE_MAX_PARALLEL : 1;
}
// Calculate total chunks
this.totalChunks = (int) Math.ceil((double) file.length() / chunkSize);
@@ -140,7 +146,8 @@ public class ChunkedUploadManager {
Log.d(TAG, "Created chunked upload manager for file: " + file.getName());
Log.d(TAG, "Total size: " + totalBytes + " bytes, Chunks: " + totalChunks);
Log.d(TAG, "Connection type: " + (isWifiConnection ? "WiFi" : "Mobile") +
", Chunk size: " + chunkSize + " bytes");
", Chunk size: " + chunkSize + " bytes" +
", Parallel uploads: " + maxParallelUploads + (parallelEnabled ? " (enabled)" : " (disabled)"));
}
/**
@@ -686,8 +693,15 @@ public class ChunkedUploadManager {
Log.e(TAG, "Server missing chunks: " + serverMissingChunks + ", reuploading missing chunks");
// Reset these chunks as not uploaded so they'll be retried
// Also subtract their bytes from uploadedBytes to prevent progress > 100%
for (Integer chunkIndex : serverMissingChunks) {
completedChunks.remove(chunkIndex);
// Calculate chunk size and subtract from uploaded bytes
long start = (long) chunkIndex * chunkSize;
long end = Math.min(start + chunkSize, file.length());
long chunkBytes = end - start;
uploadedBytes.addAndGet(-chunkBytes);
}
// Retry uploading the missing chunks
@@ -806,8 +820,15 @@ public class ChunkedUploadManager {
". Will retry these chunks specifically.");
// Remove these chunks from completedChunks
// Also subtract their bytes from uploadedBytes to prevent progress > 100%
for (Integer chunkIndex : missingChunks) {
completedChunks.remove(chunkIndex);
// Calculate chunk size and subtract from uploaded bytes
long start = (long) chunkIndex * chunkSize;
long end = Math.min(start + chunkSize, file.length());
long chunkBytes = end - start;
uploadedBytes.addAndGet(-chunkBytes);
}
retrySpecificChunks(missingChunks)

View File

@@ -20,6 +20,7 @@ public class PreferencesUtil {
public static final String KEY_ASK_BEFORE_UPLOAD = "ask_before_upload";
public static final String KEY_UPLOAD_IN_PROGRESS = "upload_in_progress";
public static final String KEY_UPLOAD_FILE_NAME = "upload_file_name";
public static final String KEY_ENABLE_PARALLEL_UPLOADS = "enable_parallel_uploads";
// Default values
private static final String DEFAULT_SERVER_ADDRESS = "";
@@ -30,6 +31,7 @@ public class PreferencesUtil {
private static final int DEFAULT_MAX_DOWNLOADS = 5;
private static final int DEFAULT_EXPIRY_HOURS = 24;
private static final boolean DEFAULT_ASK_BEFORE_UPLOAD = true;
private static final boolean DEFAULT_ENABLE_PARALLEL_UPLOADS = false;
/**
* Get the shared preferences instance
@@ -140,6 +142,7 @@ public class PreferencesUtil {
editor.putInt(KEY_DEFAULT_MAX_DOWNLOADS, DEFAULT_MAX_DOWNLOADS);
editor.putInt(KEY_DEFAULT_EXPIRY_HOURS, DEFAULT_EXPIRY_HOURS);
editor.putBoolean(KEY_ASK_BEFORE_UPLOAD, DEFAULT_ASK_BEFORE_UPLOAD);
editor.putBoolean(KEY_ENABLE_PARALLEL_UPLOADS, DEFAULT_ENABLE_PARALLEL_UPLOADS);
editor.apply();
}
@@ -295,4 +298,24 @@ public class PreferencesUtil {
public static String getUploadFileName(Context context) {
return getPreferences(context).getString(KEY_UPLOAD_FILE_NAME, null);
}
/**
* Get the parallel uploads enabled preference
*
* @param context The application context
* @return True if parallel uploads are enabled, false otherwise
*/
public static boolean isParallelUploadsEnabled(Context context) {
return getPreferences(context).getBoolean(KEY_ENABLE_PARALLEL_UPLOADS, DEFAULT_ENABLE_PARALLEL_UPLOADS);
}
/**
* Set the parallel uploads enabled preference
*
* @param context The application context
* @param enabled True to enable parallel uploads, false otherwise
*/
public static void setParallelUploadsEnabled(Context context, boolean enabled) {
getPreferences(context).edit().putBoolean(KEY_ENABLE_PARALLEL_UPLOADS, enabled).apply();
}
}