我的网站使用 html2pdf(javascript) 从 html 生成 pdf,但在 Android 上下载存在问题
webview
:kotlin。当我想在显示我的网站按钮的 webview
中单击下载时,它就会崩溃。我希望你们能经历为什么会发生这种情况以及如何克服这个问题?我还是这个 android studio 开发的初学者。
在网站中(javascript):
function generatePDF(){
var station_element = document.getElementById('station_content');
var inspect_element = document.getElementById('inspect_content');
window.scrollTo(0, 0);
// Delay execution for 500 milliseconds (adjust as needed)
setTimeout(function() {
if (station_element) {
var opt = {
margin: 50,
filename: 'myfile.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 5, width: 1300 },
jsPDF: { unit: 'pt', format: 'a4', orientation: 'portrait' }
};
html2pdf().set(opt).from(station_element).save();
} else if (inspect_element) {
var opt = {
margin: 60,
filename: 'myfile.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'pt', format: 'a4', orientation: 'portrait' }
};
html2pdf().set(opt).from(inspect_element).save();
}
}, 500); // 500 milliseconds delay
}
Android 工作室:MainActivity.kt
package com.coding.meet.webviewtoapp
import android.app.Dialog
import android.app.DownloadManager
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.view.KeyEvent
import android.webkit.CookieManager
import android.webkit.URLUtil
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.coding.meet.webviewtoapp.databinding.ActivityMainBinding
import com.google.android.material.snackbar.Snackbar
import java.io.File
class MainActivity : AppCompatActivity() {
private var webUrl = "https://smartappx.site"
private val multiplePermissionId = 14
private val multiplePermissionNameList =
if (Build.VERSION.SDK_INT >= 33) {
arrayListOf()
} else {
arrayListOf(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
}
private var isLoaded = false
private var doubleBackToExitPressedOnce = false
private val networkConnectivityObserver: NetworkConnectivityObserver by lazy {
NetworkConnectivityObserver(this)
}
private val loadingDialog: Dialog by lazy { Dialog(this) }
private val mainBinding: ActivityMainBinding by lazy {
DataBindingUtil.setContentView(this, R.layout.activity_main)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadingDialog.setContentView(R.layout.loading_layout)
loadingDialog.window!!.setLayout(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
loadingDialog.setCancelable(false)
loadingDialog.show()
val setting = mainBinding.webView.settings
setting.javaScriptEnabled = true
setting.allowFileAccess = true
setting.domStorageEnabled = true
setting.javaScriptCanOpenWindowsAutomatically = true
setting.supportMultipleWindows()
val snackbar =
Snackbar.make(mainBinding.root, "No Internet Connection", Snackbar.LENGTH_INDEFINITE)
.setAction("Wifi") { startActivity(Intent(Settings.ACTION_WIFI_SETTINGS)) }
networkConnectivityObserver.observe(this) {
when (it) {
Status.Available -> {
if (snackbar.isShown) {
snackbar.dismiss()
}
mainBinding.swipeRefresh.isEnabled = true
if (!isLoaded) loadWebView()
}
else -> {
showNoInternet()
snackbar.show()
mainBinding.swipeRefresh.isRefreshing = false
}
}
}
mainBinding.swipeRefresh.setOnRefreshListener {
if (!isLoaded) {
loadWebView()
} else {
setProgressDialogVisibility(false)
}
}
}
private fun setProgressDialogVisibility(visible: Boolean) {
if (visible) {
loadingDialog.show()
} else {
loadingDialog.dismiss()
mainBinding.swipeRefresh.isRefreshing = false
}
}
private fun showNoInternet() {
isLoaded = false
setProgressDialogVisibility(false)
gone(mainBinding.webView)
visible(mainBinding.noInternet.noInternetRL)
}
private fun loadWebView() {
gone(mainBinding.noInternet.noInternetRL)
visible(mainBinding.webView)
mainBinding.webView.loadUrl(webUrl)
mainBinding.webView.setDownloadListener {
url,
userAgent,
contentDisposition,
mimeType,
contentLength ->
Log.d("Url", url.trim())
Log.d("userAgent", userAgent)
Log.d("contentDisposition", contentDisposition)
Log.d("mimeType", mimeType)
Log.d("contentLength", contentLength.toString())
if (checkMultiplePermission()) {
download(url.trim(), userAgent, contentDisposition, mimeType, contentLength)
}
}
mainBinding.webView.webViewClient =
object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
setProgressDialogVisibility(true)
super.onPageStarted(view, url, favicon)
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?,
): Boolean {
val url = request?.url.toString()
view?.loadUrl(url)
return super.shouldOverrideUrlLoading(view, request)
}
override fun onPageFinished(view: WebView?, url: String?) {
isLoaded = true
webUrl = url!!
setProgressDialogVisibility(false)
super.onPageFinished(view, url)
}
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?,
) {
isLoaded = false
setProgressDialogVisibility(false)
super.onReceivedError(view, request, error)
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (mainBinding.webView.canGoBack()) {
mainBinding.webView.goBack()
} else {
showToastExit()
}
return true
}
}
return super.onKeyDown(keyCode, event)
}
private fun showToastExit() {
when {
doubleBackToExitPressedOnce -> {
finish()
}
else -> {
doubleBackToExitPressedOnce = true
Toast.makeText(this, "Please Click Back Again to Exit", Toast.LENGTH_LONG).show()
Handler(Looper.getMainLooper())
.postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
}
}
}
private fun download(
url: String,
userAgent: String,
contentDisposition: String,
mimeType: String,
contentLength: Long
) {
val folder = File(Environment.getExternalStorageDirectory().toString() + "/Download/Image")
if (!folder.exists()) {
folder.mkdirs()
}
Toast.makeText(this, "Download Started", Toast.LENGTH_SHORT).show()
val request = DownloadManager.Request(Uri.parse(url))
request.setMimeType(mimeType)
val cookie = CookieManager.getInstance().getCookie(url)
request.addRequestHeader("cookie", cookie)
request.addRequestHeader("User-Agent", userAgent)
request.setAllowedNetworkTypes(
DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE
)
val fileName = URLUtil.guessFileName(url, contentDisposition, mimeType)
request.setTitle(fileName)
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
)
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
"Image/$fileName"
)
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
private fun checkMultiplePermission(): Boolean {
val listPermissionNeeded = arrayListOf<String>()
for (permission in multiplePermissionNameList) {
if (
ContextCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED
) {
listPermissionNeeded.add(permission)
}
}
if (listPermissionNeeded.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
listPermissionNeeded.toTypedArray(),
multiplePermissionId
)
return false
}
return true
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == multiplePermissionId) {
if (grantResults.isNotEmpty()) {
var isGrant = true
for (element in grantResults) {
if (element == PackageManager.PERMISSION_DENIED) {
isGrant = false
}
}
if (isGrant) {
// here all permission granted successfully
Toast.makeText(this, "all permission granted successfully", Toast.LENGTH_LONG)
.show()
} else {
var someDenied = false
for (permission in permissions) {
if (
!ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
) {
if (
ActivityCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_DENIED
) {
someDenied = true
}
}
}
if (someDenied) {
// here app Setting open because all permission is not granted
// and permanent denied
appSettingOpen(this)
} else {
// here warning permission show
warningPermissionDialog(this) { _: DialogInterface, which: Int ->
when (which) {
DialogInterface.BUTTON_POSITIVE -> checkMultiplePermission()
}
}
}
}
}
}
}
}
这个代码不是我的,我只是用我的演示网站测试它。
我尝试在android平台上寻找解决方案,但没有找到。
这是android logcat
Logcat IllegalArgumentException:
024-06-03 17:28:30.668 1264-2123 AlarmManager com.google.android.gms.persistent W alarm window unlikely to be respected [CONTEXT service_id=231 ] (Ask Gemini)
java.lang.IllegalArgumentException: alarm "NetworkLocationLocator" has short window length
at bngl.b(:com.google.android.gms@[email protected] (190800-633713831):52)
at efni.f(:com.google.android.gms@[email protected] (190800-633713831):119)
at efjs.r(:com.google.android.gms@[email protected] (190800-633713831):107)
at efjs.f(:com.google.android.gms@[email protected] (190800-633713831):27)
at efjp.apply(:com.google.android.gms@[email protected] (190800-633713831):19)
at esdc.d(:com.google.android.gms@[email protected] (190800-633713831):3)
at esdd.run(:com.google.android.gms@[email protected] (190800-633713831):42)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at bomx.nL(:com.google.android.gms@[email protected] (190800-633713831):1)
at bomx.dispatchMessage(:com.google.android.gms@[email protected] (190800-633713831):138)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.os.HandlerThread.run(HandlerThread.java:67)
--------- beginning of crash
2024-06-03 17:36:43.011 1639-1639 AndroidRuntime com.coding.meet.webviewtoapp E FATAL EXCEPTION: main (Ask Gemini)
Process: com.coding.meet.webviewtoapp, PID: 1639
java.lang.IllegalArgumentException: Can only download HTTP/HTTPS URIs: blob:https://smartappx.site/ac776f6d-9ba9-4d15-ab60-2249b485853b
at android.app.DownloadManager$Request.<init>(DownloadManager.java:468)
at com.coding.meet.webviewtoapp.MainActivity.download(MainActivity.kt:236)
at com.coding.meet.webviewtoapp.MainActivity.loadWebView$lambda$2(MainActivity.kt:148)
at com.coding.meet.webviewtoapp.MainActivity.$r8$lambda$17rajA2X3B1OvSyO9osdba9FNsQ(Unknown Source:0)
at com.coding.meet.webviewtoapp.MainActivity$$ExternalSyntheticLambda1.onDownloadStart(Unknown Source:7)
at g9.handleMessage(chromium-TrichromeWebViewGoogle6432.apk-stable-447211487:145)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
2024-06-03 17:39:19.433 1264-2123 AlarmManager com.google.android.gms.persistent W alarm window unlikely to be respected [CONTEXT service_id=231 ] (Ask Gemini)
java.lang.IllegalArgumentException: alarm "NetworkLocationLocator" has short window length
at bngl.b(:com.google.android.gms@[email protected] (190800-633713831):52)
at efni.f(:com.google.android.gms@[email protected] (190800-633713831):119)
at efjs.r(:com.google.android.gms@[email protected] (190800-633713831):107)
at efjs.f(:com.google.android.gms@[email protected] (190800-633713831):27)
at efjp.apply(:com.google.android.gms@[email protected] (190800-633713831):19)
at esdc.d(:com.google.android.gms@[email protected] (190800-633713831):3)
at esdd.run(:com.google.android.gms@[email protected] (190800-633713831):42)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at bomx.nL(:com.google.android.gms@[email protected] (190800-633713831):1)
at bomx.dispatchMessage(:com.google.android.gms@[email protected] (190800-633713831):138)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.os.HandlerThread.run(HandlerThread.java:67)
我遇到了类似的问题,
onDownloadStart
参数网址附加了blob
。它只是在网址中,所以我删除了它,一切正常。
private fun download(
url: String,
userAgent: String,
contentDisposition: String,
mimeType: String,
contentLength: Long
) {
val folder = File(Environment.getExternalStorageDirectory().toString() + "/Download/Image")
if (!folder.exists()) {
folder.mkdirs()
}
Toast.makeText(this, "Download Started", Toast.LENGTH_SHORT).show()
val request = DownloadManager.Request(Uri.parse(url))
request.setMimeType(mimeType)
val cookie = CookieManager.getInstance().getCookie(url)
request.addRequestHeader("cookie", cookie)
request.addRequestHeader("User-Agent", userAgent)
request.setAllowedNetworkTypes(
DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE
)
val fileName = URLUtil.guessFileName(url, contentDisposition, mimeType)
request.setTitle(fileName)
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
)
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
"Image/$fileName"
)
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
来自方法 -> blob 的 URL:https://smartappx.site/ac776f6d-9ba9-4d15-ab60-2249b485853b
您必须使用的网址 -> https://smartappx.site/ac776f6d-9ba9-4d15-ab60-2249b485853b