diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/workouts/WorkoutDetailsFragment.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/workouts/WorkoutDetailsFragment.kt index ae370d335c..cb6fda1f59 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/workouts/WorkoutDetailsFragment.kt +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/workouts/WorkoutDetailsFragment.kt @@ -750,10 +750,15 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider { val workoutName = currentWorkout!!.summary.name val activityKind = ActivityKind.fromCode(currentWorkout!!.summary.activityKind) val activityTrackProvider = gbDevice.deviceCoordinator.getActivityTrackProvider(gbDevice, requireContext()) - val gpxFile = ActivitySummaryUtils.getShareableGpxFile(activityTrackProvider, workout.summary) - if (gpxFile == null) { - GB.toast(getString(R.string.no_gpx_track_in_activity_toast), Toast.LENGTH_LONG, GB.INFO) + val activityFile = if (workout.summary.rawDetailsPath?.endsWith(".fit") == true) { + 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 } @@ -763,7 +768,7 @@ class WorkoutDetailsFragment : Fragment(), MenuProvider { val apiClient = EndurainApiClient(serverUrl!!, endurainVm.tokenManager) endurainVm.tokenManager.performTokenRefresh(serverUrl) { LOG.info("Uploading workout '{}' (type {}) to Endurain", workoutName, activityKind) - apiClient.uploadActivity(gpxFile) { newId -> + apiClient.uploadActivity(activityFile) { newId -> if (newId != null) { // Update activity type on the server apiClient.editActivity(newId, activityKind, workoutName) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/InternetUtils.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/InternetUtils.kt index 3c7c893203..90451f39c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/InternetUtils.kt +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/InternetUtils.kt @@ -171,17 +171,17 @@ class InternetUtils { } val boundary = "----GadgetbridgeFormBoundary${System.currentTimeMillis()}" - val multipartBody = buildMultipartBody(file, fileName, mimeType, boundary) + val multipartBodyBytes = buildMultipartBody(file, fileName, mimeType, boundary) val headers = requestHeaders.toMutableMap() headers["Content-Type"] = "multipart/form-data; boundary=$boundary" val response = if (GBApplication.hasDirectInternetAccess()) { - directRequest( + directBinaryRequest( uri = uri, method = "POST", requestHeaders = headers, - body = multipartBody, + body = multipartBodyBytes, allowInsecure = allowInsecure ) } else { @@ -189,7 +189,7 @@ class InternetUtils { uri, HttpRequest.Method.POST, headers, - multipartBody.toByteArray(), + multipartBodyBytes, allowInsecure ) } @@ -207,24 +207,57 @@ class InternetUtils { fileName: String, mimeType: String, boundary: String - ): String { + ): ByteArray { val fileBytes = file.readBytes() 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("\r\n--$boundary--\r\n".toByteArray(Charsets.US_ASCII)) - writer.write("\r\n") - writer.write("--$boundary--\r\n") - writer.flush() + return output.toByteArray() + } - return output.toString("UTF-8") + /** + * Direct HTTP request using OkHttp with a binary body. + */ + private fun directBinaryRequest( + uri: Uri, + method: String, + requestHeaders: Map, + 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()) + ) + } } /** diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a35735053..30c223ee18 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4970,6 +4970,7 @@ Log out of Endurain Tap here to log out, this disables the integration No GPX track in this activity + No activity track in this activity Unable to display GPX track: %1$s Unable to share GPX track: %1$s Successfully uploaded to Endurain