removed tinyurl logic added support for new short code logic

This commit is contained in:
2025-04-14 21:37:18 -04:00
parent efa32f2831
commit 45dc412cb7
12 changed files with 653 additions and 33 deletions

View File

@@ -14,7 +14,10 @@
android:label="SimpleLogUpload"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"> <!-- Add this line -->
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar">
<activity
android:name=".MainActivity"
android:exported="true">

View File

@@ -2,4 +2,28 @@ package com.mattintech.simplelogupload;
public class Constant {
public static final String APP_TAG = "SLU::";
// Server constants
public static final String SERVER_URL = "http://172.16.100.167:7777/upload"; // HTTP protocol explicitly specified
// File size limits
public static final long MAX_FILE_SIZE = 2L * 1024 * 1024 * 1024; // 2GB
// Upload parameters
public static final String PARAM_FILE = "file";
public static final String PARAM_MAX_DOWNLOADS = "max_downloads";
public static final String PARAM_EXPIRY_HOURS = "expiry_hours";
// Default values
public static final int DEFAULT_MAX_DOWNLOADS = 1;
public static final int DEFAULT_EXPIRY_HOURS = 24;
public static final int MAX_EXPIRY_HOURS = 24;
// Broadcast actions
public static final String ACTION_UPLOAD_COMPLETE = "com.mattintech.simplelogupload.UPLOAD_COMPLETE";
public static final String ACTION_UPLOAD_ERROR = "com.mattintech.simplelogupload.UPLOAD_ERROR";
// Intent extras
public static final String EXTRA_UPLOAD_CODE = "upload_code";
public static final String EXTRA_UPLOAD_ERROR = "upload_error";
}

View File

@@ -19,6 +19,12 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import org.json.JSONException;
import org.json.JSONObject;
import com.mattintech.simplelogupload.model.UploadResult;
import okhttp3.Call;
import okhttp3.Callback;
@@ -52,6 +58,7 @@ public class FileUploadForegroundService extends Service {
uploadFile(mostRecentFile);
} else {
Log.e(TAG, "No DUMPSTATE file found.");
sendErrorBroadcast("No DUMPSTATE file found");
stopSelf();
}
@@ -98,17 +105,34 @@ public class FileUploadForegroundService extends Service {
}
private void uploadFile(File file) {
OkHttpClient client = new OkHttpClient();
// Validate file size before upload
if (file.length() > Constant.MAX_FILE_SIZE) {
Log.e(TAG, "File too large: " + file.length() + " bytes (max: " + Constant.MAX_FILE_SIZE + " bytes)");
sendErrorBroadcast("File size exceeds the maximum allowed size of 2GB");
stopSelf();
return;
}
// Create OkHttpClient with timeout configuration for large files
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS) // 3 minutes timeout for large uploads
.readTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Log.d(TAG, "Uploading file: " + file.getName() + ", Size: " + file.length());
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/zip"));
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), fileBody)
.addFormDataPart(Constant.PARAM_FILE, file.getName(), fileBody)
.addFormDataPart(Constant.PARAM_MAX_DOWNLOADS, String.valueOf(Constant.DEFAULT_MAX_DOWNLOADS))
.addFormDataPart(Constant.PARAM_EXPIRY_HOURS, String.valueOf(Constant.DEFAULT_EXPIRY_HOURS))
.build();
Request request = new Request.Builder()
.url("http://172.16.30.69:7777/upload") // Replace with your server's URL
.url(Constant.SERVER_URL)
.post(requestBody)
.build();
@@ -116,20 +140,59 @@ public class FileUploadForegroundService extends Service {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.e(TAG, "Upload failed: " + e.getMessage());
String errorMsg = "Upload failed: " + e.getMessage();
Log.e(TAG, errorMsg);
sendErrorBroadcast(errorMsg);
stopSelf();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String tinyUrl = response.body().string(); // Assuming the server returns tinyURL in response
Log.d(TAG, "Upload successful! TinyURL: " + tinyUrl);
try {
String responseBody = response.body().string();
Log.d(TAG, "Server response: " + responseBody);
// Parse JSON response to get the upload code
JSONObject jsonResponse = new JSONObject(responseBody);
String uploadCode = jsonResponse.getString("code");
Log.d(TAG, "Upload successful! Code: " + uploadCode);
sendSuccessBroadcast(uploadCode);
} catch (JSONException e) {
Log.e(TAG, "Failed to parse server response", e);
sendErrorBroadcast("Failed to parse server response");
}
} else {
Log.e(TAG, "Upload failed, Response code: " + response.code());
String errorBody = response.body().string();
String errorMsg = "Upload failed, Response code: " + response.code();
Log.e(TAG, errorMsg + ", Body: " + errorBody);
try {
JSONObject errorJson = new JSONObject(errorBody);
if (errorJson.has("error")) {
errorMsg = errorJson.getString("error");
}
} catch (JSONException e) {
// If parsing fails, use the default error message
}
sendErrorBroadcast(errorMsg);
}
stopSelf();
}
});
}
private void sendSuccessBroadcast(String uploadCode) {
Intent intent = new Intent(Constant.ACTION_UPLOAD_COMPLETE);
intent.putExtra(Constant.EXTRA_UPLOAD_CODE, uploadCode);
sendBroadcast(intent);
}
private void sendErrorBroadcast(String errorMessage) {
Intent intent = new Intent(Constant.ACTION_UPLOAD_ERROR);
intent.putExtra(Constant.EXTRA_UPLOAD_ERROR, errorMessage);
sendBroadcast(intent);
}
}

View File

@@ -2,6 +2,7 @@ package com.mattintech.simplelogupload;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
@@ -10,6 +11,10 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import org.json.JSONException;
import org.json.JSONObject;
import okhttp3.Call;
import okhttp3.Callback;
@@ -33,6 +38,8 @@ public class FileUploadJobService extends JobService {
uploadFile(mostRecentFile, params);
} else {
Toast.makeText(getApplicationContext(), "No DUMPSTATE file found.", Toast.LENGTH_SHORT).show();
sendErrorBroadcast("No DUMPSTATE file found");
jobFinished(params, false);
}
return true;
@@ -49,18 +56,36 @@ public class FileUploadJobService extends JobService {
.max(Comparator.comparingLong(File::lastModified))
.orElse(null) : null;
}
private void uploadFile(File file, JobParameters params) {
OkHttpClient client = new OkHttpClient();
// Validate file size before upload
if (file.length() > Constant.MAX_FILE_SIZE) {
Log.e(TAG, "File too large: " + file.length() + " bytes (max: " + Constant.MAX_FILE_SIZE + " bytes)");
sendErrorBroadcast("File size exceeds the maximum allowed size of 2GB");
jobFinished(params, false);
return;
}
// Create OkHttpClient with timeout configuration for large files
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS) // 3 minutes timeout for large uploads
.readTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Log.d(TAG, "Uploading file: " + file.getName() + ", Size: " + file.length());
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/zip"));
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/zip"));
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), fileBody)
.addFormDataPart(Constant.PARAM_FILE, file.getName(), fileBody)
.addFormDataPart(Constant.PARAM_MAX_DOWNLOADS, String.valueOf(Constant.DEFAULT_MAX_DOWNLOADS))
.addFormDataPart(Constant.PARAM_EXPIRY_HOURS, String.valueOf(Constant.DEFAULT_EXPIRY_HOURS))
.build();
Request request = new Request.Builder()
.url("http://172.16.30.69:7777/upload") // Replace with your Flask server's URL
.url(Constant.SERVER_URL)
.post(requestBody)
.build();
@@ -68,25 +93,59 @@ public class FileUploadJobService extends JobService {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.e(TAG, "Upload failed: " + e.getMessage());
String errorMsg = "Upload failed: " + e.getMessage();
Log.e(TAG, errorMsg);
sendErrorBroadcast(errorMsg);
jobFinished(params, false);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String tinyUrl = response.body().string(); // Assuming the server returns tinyURL in response
Log.d(TAG, "Upload successful! TinyURL: " + tinyUrl);
// Show tinyURL on the UI (using BroadcastReceiver or local storage to communicate with MainActivity)
try {
String responseBody = response.body().string();
Log.d(TAG, "Server response: " + responseBody);
// Parse JSON response to get the upload code
JSONObject jsonResponse = new JSONObject(responseBody);
String uploadCode = jsonResponse.getString("code");
Log.d(TAG, "Upload successful! Code: " + uploadCode);
sendSuccessBroadcast(uploadCode);
} catch (JSONException e) {
Log.e(TAG, "Failed to parse server response", e);
sendErrorBroadcast("Failed to parse server response");
}
} else {
Log.e(TAG, "Upload failed, Response code: " + response.code());
String errorBody = response.body().string();
String errorMsg = "Upload failed, Response code: " + response.code();
Log.e(TAG, errorMsg + ", Body: " + errorBody);
try {
JSONObject errorJson = new JSONObject(errorBody);
if (errorJson.has("error")) {
errorMsg = errorJson.getString("error");
}
} catch (JSONException e) {
// If parsing fails, use the default error message
}
sendErrorBroadcast(errorMsg);
}
jobFinished(params, false);
}
});
}
private void sendSuccessBroadcast(String uploadCode) {
Intent intent = new Intent(Constant.ACTION_UPLOAD_COMPLETE);
intent.putExtra(Constant.EXTRA_UPLOAD_CODE, uploadCode);
sendBroadcast(intent);
}
private void sendErrorBroadcast(String errorMessage) {
Intent intent = new Intent(Constant.ACTION_UPLOAD_ERROR);
intent.putExtra(Constant.EXTRA_UPLOAD_ERROR, errorMessage);
sendBroadcast(intent);
}
}

View File

@@ -5,10 +5,11 @@ import android.Manifest;
import android.app.Activity;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.app.job.JobParameters;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
@@ -17,6 +18,7 @@ import android.os.Environment;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
@@ -31,7 +33,11 @@ public class MainActivity extends Activity {
private static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 101;
private TextView resultView;
private Button uploadButton;
private ProgressBar progressBar;
private File mostRecentDumpStateFile;
// BroadcastReceiver to listen for upload results
private BroadcastReceiver uploadReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -40,7 +46,7 @@ public class MainActivity extends Activity {
resultView = findViewById(R.id.resultView);
uploadButton = findViewById(R.id.uploadButton);
progressBar = findViewById(R.id.progressBar);
// Check for All Files Access Permission
requestNotificationPermission();
@@ -54,8 +60,50 @@ public class MainActivity extends Activity {
if (mostRecentDumpStateFile != null) {
//scheduleJob();
startFileUpload();
progressBar.setVisibility(View.VISIBLE);
uploadButton.setEnabled(false);
resultView.setText("Uploading file...");
}
});
// Initialize broadcast receiver
uploadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
progressBar.setVisibility(View.GONE);
uploadButton.setEnabled(true);
if (intent.getAction().equals(Constant.ACTION_UPLOAD_COMPLETE)) {
String uploadCode = intent.getStringExtra(Constant.EXTRA_UPLOAD_CODE);
showUploadSuccessDialog(uploadCode);
} else if (intent.getAction().equals(Constant.ACTION_UPLOAD_ERROR)) {
String errorMessage = intent.getStringExtra(Constant.EXTRA_UPLOAD_ERROR);
Toast.makeText(MainActivity.this, "Upload Error: " + errorMessage, Toast.LENGTH_LONG).show();
resultView.setText("Upload failed: " + errorMessage);
}
}
};
}
@Override
protected void onResume() {
super.onResume();
// Register the broadcast receiver
IntentFilter filter = new IntentFilter();
filter.addAction(Constant.ACTION_UPLOAD_COMPLETE);
filter.addAction(Constant.ACTION_UPLOAD_ERROR);
registerReceiver(uploadReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
// Unregister the broadcast receiver
try {
unregisterReceiver(uploadReceiver);
} catch (IllegalArgumentException e) {
// Receiver not registered
}
}
private void requestNotificationPermission() {
@@ -94,7 +142,7 @@ public class MainActivity extends Activity {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_MANAGE_EXTERNAL_STORAGE) {
if (hasAllFilesAccessPermission()) {
scheduleJob(); // Now you can proceed if permission is granted
checkForDumpStateAndUpdateUI();
} else {
Toast.makeText(this, "Permission not granted!", Toast.LENGTH_SHORT).show();
}
@@ -138,7 +186,10 @@ public class MainActivity extends Activity {
} else {
startService(serviceIntent);
}
}
private void showUploadSuccessDialog(String uploadCode) {
UploadResultDialog dialog = new UploadResultDialog(this, uploadCode);
dialog.show();
}
}

View File

@@ -0,0 +1,39 @@
package com.mattintech.simplelogupload;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class UploadResultDialog extends Dialog {
private String uploadCode;
public UploadResultDialog(Context context, String uploadCode) {
super(context);
this.uploadCode = uploadCode;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.upload_result_dialog);
TextView codeTextView = findViewById(R.id.uploadCode);
codeTextView.setText(uploadCode);
Button copyButton = findViewById(R.id.copyButton);
copyButton.setOnClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Upload Code", uploadCode);
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), "Code copied to clipboard", Toast.LENGTH_SHORT).show();
});
}
}

View File

@@ -0,0 +1,28 @@
package com.mattintech.simplelogupload.model;
public class UploadResult {
private String code;
private String error;
public UploadResult(String code) {
this.code = code;
this.error = null;
}
public UploadResult(String error, boolean isError) {
this.code = null;
this.error = error;
}
public String getCode() {
return code;
}
public String getError() {
return error;
}
public boolean isSuccess() {
return code != null && !code.isEmpty();
}
}

View File

@@ -27,5 +27,15 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/uploadButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/dialogTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Upload Successful"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/codeLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Your upload code:"
android:textAlignment="center"
app:layout_constraintTop_toBottomOf="@id/dialogTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/uploadCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAlignment="center"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#007BFF"
app:layout_constraintTop_toBottomOf="@id/codeLabel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/copyButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Copy Code"
app:layout_constraintTop_toBottomOf="@id/uploadCode"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,12 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Allow cleartext traffic to specific server IP -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">172.16.30.69</domain> <!-- Replace with your server -->
</domain-config>
<!-- OR: To allow for all domains (for testing purposes only) -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">172.16.100.167</domain>
<!-- Add any other specific servers below if needed -->
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">192.168.1.1</domain>
<domain includeSubdomains="true">*.*</domain>
<domain includeSubdomains="true">10.0.2.2</domain> <!-- Android emulator localhost -->
</domain-config>
<!-- Base config for all other domains -->
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>