diff --git a/evershelf-kiosk/app/src/main/AndroidManifest.xml b/evershelf-kiosk/app/src/main/AndroidManifest.xml index c754b2e..4a15cd9 100644 --- a/evershelf-kiosk/app/src/main/AndroidManifest.xml +++ b/evershelf-kiosk/app/src/main/AndroidManifest.xml @@ -9,6 +9,17 @@ + + + + + + + + + + + diff --git a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/KioskActivity.kt b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/KioskActivity.kt index be25c6a..3fc19b5 100644 --- a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/KioskActivity.kt +++ b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/KioskActivity.kt @@ -1,6 +1,7 @@ package it.dadaloop.evershelf.kiosk import android.annotation.SuppressLint +import android.Manifest import android.app.ActivityManager import android.content.Context import android.content.Intent @@ -33,6 +34,8 @@ import android.widget.ScrollView import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import com.google.android.material.button.MaterialButton import org.json.JSONObject import java.net.URL @@ -69,8 +72,12 @@ class KioskActivity : AppCompatActivity() { // File chooser private var fileChooserCallback: ValueCallback>? = null + // Pending WebView permission request (waiting for runtime grant) + private var pendingWebPermission: PermissionRequest? = null + companion object { private const val FILE_CHOOSER_REQUEST = 1002 + private const val PERMISSION_REQUEST_CODE = 1003 private const val PREFS_NAME = "evershelf_kiosk" private const val KEY_URL = "evershelf_url" private const val KEY_SETUP_COMPLETE = "setup_complete" @@ -89,6 +96,7 @@ class KioskActivity : AppCompatActivity() { bindViews() enterImmersiveMode() enableKioskLock() + requestAllPermissions() // Show splash then proceed Handler(Looper.getMainLooper()).postDelayed({ @@ -171,6 +179,46 @@ class KioskActivity : AppCompatActivity() { } } + // ── Runtime Permissions ───────────────────────────────────────────── + + private fun requestAllPermissions() { + val needed = mutableListOf() + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.CAMERA) + } + if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.RECORD_AUDIO) + } + if (Build.VERSION.SDK_INT >= 33) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.READ_MEDIA_IMAGES) + } + } else if (Build.VERSION.SDK_INT <= 32) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + if (needed.isNotEmpty()) { + ActivityCompat.requestPermissions(this, needed.toTypedArray(), PERMISSION_REQUEST_CODE) + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_REQUEST_CODE) { + // Grant pending WebView permission if camera/mic were just granted + pendingWebPermission?.let { req -> + val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } + if (allGranted) { + req.grant(req.resources) + } else { + req.deny() + } + pendingWebPermission = null + } + } + } + // ── Triple-tap to exit ──────────────────────────────────────────────── private fun handleTripleTap() { @@ -417,7 +465,30 @@ class KioskActivity : AppCompatActivity() { webView.webChromeClient = object : WebChromeClient() { override fun onPermissionRequest(request: PermissionRequest?) { - runOnUiThread { request?.grant(request.resources) } + request ?: return + runOnUiThread { + val needed = mutableListOf() + for (res in request.resources) { + when (res) { + PermissionRequest.RESOURCE_VIDEO_CAPTURE -> { + if (ContextCompat.checkSelfPermission(this@KioskActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.CAMERA) + } + } + PermissionRequest.RESOURCE_AUDIO_CAPTURE -> { + if (ContextCompat.checkSelfPermission(this@KioskActivity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + needed.add(Manifest.permission.RECORD_AUDIO) + } + } + } + } + if (needed.isEmpty()) { + request.grant(request.resources) + } else { + pendingWebPermission = request + ActivityCompat.requestPermissions(this@KioskActivity, needed.toTypedArray(), PERMISSION_REQUEST_CODE) + } + } } override fun onConsoleMessage(msg: ConsoleMessage?): Boolean = true override fun onShowFileChooser(