fix: download PDF/DOCX exports through authenticated client instead of browser
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
5754fcf445
commit
e90af27e66
@ -34,6 +34,16 @@
|
|||||||
android:name=".ui.AdminActivity"
|
android:name=".ui.AdminActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -313,4 +313,21 @@ object ApiClient {
|
|||||||
|
|
||||||
fun getExportPdfUrl(jobId: String): String = "$API_BASE/export/$jobId/pdf"
|
fun getExportPdfUrl(jobId: String): String = "$API_BASE/export/$jobId/pdf"
|
||||||
fun getExportDocxUrl(jobId: String): String = "$API_BASE/export/$jobId/docx"
|
fun getExportDocxUrl(jobId: String): String = "$API_BASE/export/$jobId/docx"
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun downloadExport(url: String): ByteArray {
|
||||||
|
val req = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
client.newCall(req).execute().use { res ->
|
||||||
|
if (!res.isSuccessful) {
|
||||||
|
val errBody = res.body?.string()
|
||||||
|
val detail = try {
|
||||||
|
gson.fromJson(errBody, Map::class.java)["detail"] as? String
|
||||||
|
} catch (_: Exception) { null }
|
||||||
|
throw IOException(detail ?: "Export failed")
|
||||||
|
}
|
||||||
|
return res.body?.bytes() ?: throw IOException("Empty response")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -13,11 +13,14 @@ import android.webkit.WebView
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.example.resbuilder.R
|
import com.example.resbuilder.R
|
||||||
import com.example.resbuilder.data.model.Job
|
import com.example.resbuilder.data.model.Job
|
||||||
import com.example.resbuilder.data.remote.ApiClient
|
import com.example.resbuilder.data.remote.ApiClient
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
class ResultFragment : Fragment() {
|
class ResultFragment : Fragment() {
|
||||||
|
|
||||||
@ -86,14 +89,58 @@ class ResultFragment : Fragment() {
|
|||||||
newButton.setOnClickListener { listener?.onNewJob() }
|
newButton.setOnClickListener { listener?.onNewJob() }
|
||||||
|
|
||||||
jobId?.let { id ->
|
jobId?.let { id ->
|
||||||
pdfButton.setOnClickListener { openUrl(ApiClient.getExportPdfUrl(id)) }
|
pdfButton.setOnClickListener { downloadAndOpen(id, "pdf") }
|
||||||
docxButton.setOnClickListener { openUrl(ApiClient.getExportDocxUrl(id)) }
|
docxButton.setOnClickListener { downloadAndOpen(id, "docx") }
|
||||||
} ?: run {
|
} ?: run {
|
||||||
pdfButton.isEnabled = false
|
pdfButton.isEnabled = false
|
||||||
docxButton.isEnabled = false
|
docxButton.isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun downloadAndOpen(jobId: String, format: String) {
|
||||||
|
val url = if (format == "pdf") ApiClient.getExportPdfUrl(jobId) else ApiClient.getExportDocxUrl(jobId)
|
||||||
|
val dir = File(requireContext().cacheDir, "exports")
|
||||||
|
if (!dir.exists()) dir.mkdirs()
|
||||||
|
val safeId = jobId.replace("/", "_").replace("..", "_").take(8)
|
||||||
|
val file = File(dir, "$safeId.$format")
|
||||||
|
|
||||||
|
pdfButton.isEnabled = false
|
||||||
|
docxButton.isEnabled = false
|
||||||
|
Toast.makeText(context, R.string.exporting, Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
val bytes = ApiClient.downloadExport(url)
|
||||||
|
FileOutputStream(file).use { it.write(bytes) }
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
pdfButton.isEnabled = true
|
||||||
|
docxButton.isEnabled = true
|
||||||
|
openFile(file)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
requireActivity().runOnUiThread {
|
||||||
|
pdfButton.isEnabled = true
|
||||||
|
docxButton.isEnabled = true
|
||||||
|
Toast.makeText(context, e.message ?: getString(R.string.export_failed), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openFile(file: File) {
|
||||||
|
val uri = FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.fileprovider", file)
|
||||||
|
val mime = if (file.extension == "pdf") "application/pdf" else "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, mime)
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, R.string.export_no_viewer, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun copyText() {
|
private fun copyText() {
|
||||||
val raw = rawContent
|
val raw = rawContent
|
||||||
if (raw.isNullOrEmpty()) {
|
if (raw.isNullOrEmpty()) {
|
||||||
@ -105,11 +152,6 @@ class ResultFragment : Fragment() {
|
|||||||
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openUrl(url: String) {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOnNewListener(l: OnNewListener) {
|
fun setOnNewListener(l: OnNewListener) {
|
||||||
listener = l
|
listener = l
|
||||||
}
|
}
|
||||||
|
|||||||
6
app/src/main/res/xml/file_paths.xml
Normal file
6
app/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<cache-path
|
||||||
|
name="exports"
|
||||||
|
path="exports/" />
|
||||||
|
</paths>
|
||||||
Loading…
x
Reference in New Issue
Block a user