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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -118,15 +118,21 @@ public class ChunkedUploadManager {
|
||||
|
||||
// Set up connectivity manager for network monitoring
|
||||
this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
|
||||
// Determine initial connection type
|
||||
detectConnectionType();
|
||||
|
||||
|
||||
// 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);
|
||||
@@ -139,8 +145,9 @@ 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");
|
||||
Log.d(TAG, "Connection type: " + (isWifiConnection ? "WiFi" : "Mobile") +
|
||||
", Chunk size: " + chunkSize + " bytes" +
|
||||
", Parallel uploads: " + maxParallelUploads + (parallelEnabled ? " (enabled)" : " (disabled)"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,33 +668,40 @@ public class ChunkedUploadManager {
|
||||
// Verify all chunks are uploaded before finalizing
|
||||
boolean allChunksUploaded = true;
|
||||
Set<Integer> missingChunks = new HashSet<>();
|
||||
|
||||
|
||||
for (int i = 0; i < totalChunks; i++) {
|
||||
if (!completedChunks.contains(i)) {
|
||||
missingChunks.add(i);
|
||||
allChunksUploaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!allChunksUploaded) {
|
||||
Log.e(TAG, "Cannot finalize: missing chunks " + missingChunks);
|
||||
future.complete(new UploadResult("Cannot finalize: missing chunks " + missingChunks));
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
// Get server URL and client key from preferences
|
||||
String serverBaseUrl = PreferencesUtil.getServerBaseUrl(context);
|
||||
String clientKey = PreferencesUtil.getClientKey(context);
|
||||
|
||||
|
||||
// Before finalizing, verify with the server which chunks it has received
|
||||
verifyChunksWithServer()
|
||||
.thenAccept(serverMissingChunks -> {
|
||||
if (!serverMissingChunks.isEmpty()) {
|
||||
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
|
||||
@@ -802,12 +816,19 @@ public class ChunkedUploadManager {
|
||||
|
||||
// Automatically retry these specific chunks
|
||||
if (!missingChunks.isEmpty()) {
|
||||
Log.d(TAG, "Server reported missing chunks: " + missingChunks +
|
||||
Log.d(TAG, "Server reported missing chunks: " + missingChunks +
|
||||
". 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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user