Endurain: Upload FIT file if available

This commit is contained in:
José Rebelo
2026-06-19 23:31:23 +02:00
committed by Arjan Schrijver
parent d6f6287919
commit cc529b40e2
3 changed files with 59 additions and 20 deletions
@@ -750,10 +750,15 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
val workoutName = currentWorkout!!.summary.name val workoutName = currentWorkout!!.summary.name
val activityKind = ActivityKind.fromCode(currentWorkout!!.summary.activityKind) val activityKind = ActivityKind.fromCode(currentWorkout!!.summary.activityKind)
val activityTrackProvider = gbDevice.deviceCoordinator.getActivityTrackProvider(gbDevice, requireContext()) val activityTrackProvider = gbDevice.deviceCoordinator.getActivityTrackProvider(gbDevice, requireContext())
val gpxFile = ActivitySummaryUtils.getShareableGpxFile(activityTrackProvider, workout.summary)
if (gpxFile == null) { val activityFile = if (workout.summary.rawDetailsPath?.endsWith(".fit") == true) {
GB.toast(getString(R.string.no_gpx_track_in_activity_toast), Toast.LENGTH_LONG, GB.INFO) FileUtils.tryFixPath(workout.summary.rawDetailsPath)
} else {
ActivitySummaryUtils.getShareableGpxFile(activityTrackProvider, workout.summary)
}
if (activityFile == null) {
GB.toast(getString(R.string.no_activity_track_in_activity_toast), Toast.LENGTH_LONG, GB.INFO)
return return
} }
@@ -763,7 +768,7 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider {
val apiClient = EndurainApiClient(serverUrl!!, endurainVm.tokenManager) val apiClient = EndurainApiClient(serverUrl!!, endurainVm.tokenManager)
endurainVm.tokenManager.performTokenRefresh(serverUrl) { endurainVm.tokenManager.performTokenRefresh(serverUrl) {
LOG.info("Uploading workout '{}' (type {}) to Endurain", workoutName, activityKind) LOG.info("Uploading workout '{}' (type {}) to Endurain", workoutName, activityKind)
apiClient.uploadActivity(gpxFile) { newId -> apiClient.uploadActivity(activityFile) { newId ->
if (newId != null) { if (newId != null) {
// Update activity type on the server // Update activity type on the server
apiClient.editActivity(newId, activityKind, workoutName) apiClient.editActivity(newId, activityKind, workoutName)
@@ -171,17 +171,17 @@ class InternetUtils {
} }
val boundary = "----GadgetbridgeFormBoundary${System.currentTimeMillis()}" val boundary = "----GadgetbridgeFormBoundary${System.currentTimeMillis()}"
val multipartBody = buildMultipartBody(file, fileName, mimeType, boundary) val multipartBodyBytes = buildMultipartBody(file, fileName, mimeType, boundary)
val headers = requestHeaders.toMutableMap() val headers = requestHeaders.toMutableMap()
headers["Content-Type"] = "multipart/form-data; boundary=$boundary" headers["Content-Type"] = "multipart/form-data; boundary=$boundary"
val response = if (GBApplication.hasDirectInternetAccess()) { val response = if (GBApplication.hasDirectInternetAccess()) {
directRequest( directBinaryRequest(
uri = uri, uri = uri,
method = "POST", method = "POST",
requestHeaders = headers, requestHeaders = headers,
body = multipartBody, body = multipartBodyBytes,
allowInsecure = allowInsecure allowInsecure = allowInsecure
) )
} else { } else {
@@ -189,7 +189,7 @@ class InternetUtils {
uri, uri,
HttpRequest.Method.POST, HttpRequest.Method.POST,
headers, headers,
multipartBody.toByteArray(), multipartBodyBytes,
allowInsecure allowInsecure
) )
} }
@@ -207,24 +207,57 @@ class InternetUtils {
fileName: String, fileName: String,
mimeType: String, mimeType: String,
boundary: String boundary: String
): String { ): ByteArray {
val fileBytes = file.readBytes() val fileBytes = file.readBytes()
val output = ByteArrayOutputStream() val output = ByteArrayOutputStream()
val writer = output.bufferedWriter()
writer.write("--$boundary\r\n")
writer.write("Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"\r\n")
writer.write("Content-Type: $mimeType\r\n")
writer.write("\r\n")
writer.flush()
val header = "--$boundary\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"\r\n" +
"Content-Type: $mimeType\r\n" +
"\r\n"
output.write(header.toByteArray(Charsets.US_ASCII))
output.write(fileBytes) output.write(fileBytes)
output.write("\r\n--$boundary--\r\n".toByteArray(Charsets.US_ASCII))
writer.write("\r\n") return output.toByteArray()
writer.write("--$boundary--\r\n") }
writer.flush()
return output.toString("UTF-8") /**
* Direct HTTP request using OkHttp with a binary body.
*/
private fun directBinaryRequest(
uri: Uri,
method: String,
requestHeaders: Map<String, String>,
body: ByteArray,
allowInsecure: Boolean
): WebResourceResponse {
val client = if (allowInsecure) createInsecureClient() else defaultClient
val builder = Request.Builder().url(uri.toString())
for ((key, value) in headersWithUserAgent(requestHeaders)) {
builder.addHeader(key, value)
}
val contentType = getHeader(requestHeaders, "content-type") ?: "application/octet-stream"
val requestBody = body.toRequestBody(contentType.toMediaType())
builder.method(method.uppercase(), requestBody)
client.newCall(builder.build()).execute().use { response ->
val statusCode = response.code
val message = if (!response.message.isEmpty()) response.message else "OK"
val headers = response.headers.toMap()
val respContentType = response.header("content-type") ?: "application/octet-stream"
val encoding = response.header("content-encoding") ?: "UTF-8"
return WebResourceResponse(
respContentType,
encoding,
statusCode,
message,
headers,
ByteArrayInputStream(response.body.bytes())
)
}
} }
/** /**
+1
View File
@@ -4970,6 +4970,7 @@
<string name="pref_endurain_log_out_title">Log out of Endurain</string> <string name="pref_endurain_log_out_title">Log out of Endurain</string>
<string name="pref_endurain_log_out_summary">Tap here to log out, this disables the integration</string> <string name="pref_endurain_log_out_summary">Tap here to log out, this disables the integration</string>
<string name="no_gpx_track_in_activity_toast">No GPX track in this activity</string> <string name="no_gpx_track_in_activity_toast">No GPX track in this activity</string>
<string name="no_activity_track_in_activity_toast">No activity track in this activity</string>
<string name="unable_to_display_gpx_track_toast">Unable to display GPX track: %1$s</string> <string name="unable_to_display_gpx_track_toast">Unable to display GPX track: %1$s</string>
<string name="unable_to_share_gpx_track_toast">Unable to share GPX track: %1$s</string> <string name="unable_to_share_gpx_track_toast">Unable to share GPX track: %1$s</string>
<string name="endurain_successfully_uploaded_toast">Successfully uploaded to Endurain</string> <string name="endurain_successfully_uploaded_toast">Successfully uploaded to Endurain</string>