I am getting error "Either grant() or deny() has been already called" when I am trying to access a page in WebView that requires both Microphone and Camera access.
That's my on onPermissionRequest but for some reason if both of them are requested in the same time the app crashes but with only one request seems to work fine.
override fun onPermissionRequest(permissionRequest: PermissionRequest?) {
Log.d(TAG, "onJSPermissionRequest")
val mPermissionRequest: PermissionRequest? = permissionRequest
for (request in permissionRequest?.resources!!){
if (request == PermissionRequest.RESOURCE_VIDEO_CAPTURE){
val alertDialogBuilder = AlertDialog.Builder(mContext)
.setTitle("Allow Permission to Camera access!")
.setPositiveButton("Allow") { dialog, which ->
dialog.dismiss()
mPermissionRequest?.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
}
.setNegativeButton("Deny") { dialog, which ->
dialog.dismiss()
mPermissionRequest?.deny()
}
val alertDialog = alertDialogBuilder.create()
alertDialog.show()
}
if(request == PermissionRequest.RESOURCE_AUDIO_CAPTURE){
val alertDialogBuilder = AlertDialog.Builder(mContext)
.setTitle("Allow Permission to Microphone access!")
.setPositiveButton("Allow") { dialog, which ->
dialog.dismiss()
mPermissionRequest?.grant(arrayOf(PermissionRequest.RESOURCE_AUDIO_CAPTURE))
}
.setNegativeButton("Deny") { dialog, which ->
dialog.dismiss()
mPermissionRequest?.deny()
}
val alertDialog = alertDialogBuilder.create()
alertDialog.show()
}
}
}
override fun onPermissionRequestCanceled(request: PermissionRequest?) {
super.onPermissionRequestCanceled(request)
Toast.makeText(mContext,"Permission Denied",Toast.LENGTH_SHORT).show()
}
}
}
I know the issue is because I asking for grant twice (removing the grant command in one of the cases solves the crash but now giving permissions) but the resources are different for both requests..
Related
I’m in Android Studio version Chipmunk with one patch. I’m writing in Kotlin, a language I find is just beautiful. I have a dialog box over a fragment where I want to capture the changes in the edit text box as they happen. I’ve tried with straightforward code found here in Stack Overflow but I think the fragment can’t view the dialog box.
Following is code used:
private fun dlgFind() {
try {
//val sTxt: EditText = findViewById(R.id.txtStock)
val sTxt = binding.txtMeds
val spannable: Spannable = SpannableString(sTxt.text.toString())
sTxt.setText(spannable.toString()) // clears highlighted text
val dialog = Dialog(requireContext(), R.style.RoundedCornersDialogFind)
dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) // makes frag text readable
dialog.setContentView(R.layout.dlg_find)
// tried to max dialog window to full width of screen
//dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val btnFind = dialog.findViewById(R.id.btnGo) as ImageButton
val txtFind = dialog.findViewById(R.id.editFind) as EditText
txtFind.isFocusableInTouchMode = true
txtFind.isFocusable = true
//txtFind.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
txtFind.requestFocus()
txtFind.postDelayed({
txtFind.requestFocus()
val imm = context?.getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.showSoftInput(txtFind, InputMethodManager.SHOW_IMPLICIT)
}, 1)
btnFind.setOnClickListener { dialog.dismiss()
gsFindBoxTxt = txtFind.text.toString()
sTxt.requestFocus()
//sTxt.setText(spannable.toString()) // clears highlighted text
findWithDlg() // method finds strings
}
//dialog.setOnCancelListener { editHours() }
dialog.window?.setGravity(Gravity.BOTTOM)
dialog.show()
} catch (e: Exception) {
val methodName = object {}.javaClass.enclosingMethod?.name
Toast.makeText(context, methodName.toString(), Toast.LENGTH_LONG).show()
}
}
Attempt to read from dialog box in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
val sEditTxt = view.findViewById<EditText>(R.id.editFind)
sEditTxt?.doOnTextChanged { _, _, _, _ ->
Toast.makeText(context, "!", Toast.LENGTH_LONG).show() }
if (sEditTxt?.text != null) {
sEditTxt.doAfterTextChanged {
Toast.makeText(context, "!", Toast.LENGTH_LONG).show()
findWithDlg()
}
}
} catch (e: Exception) {
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show()
}
Note I have tried boilerplate TextWatcher code too and I can't return view in onCreate because I'm using binding throughout the fragment. The return in onCreate is to the binding.root. And even in onDestroyView the last line is _binding = null. Maybe remove all binding and revert to using views?
I've also tried these in the dialog method (function) to no avail yet...
Yes! I figured out how to listen to the text changes. I set following in the dialog method:
dialog.setOnShowListener { txtFind.doOnTextChanged { _, _, _, _ ->
Toast.makeText(context, "Text Change", Toast.LENGTH_LONG).show()
}
}
I'm trying to build an Android app in Android Studio using Kotlin to send some simple data between an ESP32 and a mobile over Bluetooth. I've been following along a number of tutorials but just can't seem to get the connection established, permissions and scanning for devices looks to be working correctly. When I call connect() on the socket the app hangs for a few seconds and then crashes with this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.btleveller, PID: 28899
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=Intent { (has extras) }} to activity {com.example.btleveller/com.example.btleveller.MainActivity}: java.io.IOException: read failed, socket might closed or timeout, read ret: -1
at android.app.ActivityThread.deliverResults(ActivityThread.java:5368)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5407)
etc... I can post the full output if it's helpful
My ESP is running some very basic helloworld style code using the NimBLE-Arduino code, programmed through VSCode with the PlatformIO extension. I think this side of it is all working correct as I can see the device in the "nRF Connect" app on my mobile. The scanning is done through the CompanionDeviceManager library:
I thought maybe there was a problem with the UUID I was supplying, or that I needed to make changes for BLE as opposed to regular Bluetooth but so far nothing I've found online has worked. I've also tried using "createL2capChannel()" to create the socket but got stuck on the PSM value. These are the relevant bits of code:
//private val ESP_UUID = UUID.fromString("0000dead-0000-1000-8000-00805F9B34FB")
private val ESP_UUID = UUID.fromString("0000baad-0000-1000-8000-00805F9B34FB")
...
// Look for connection, kicked off by button press
fun lookForConn(view: View) {
val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
.setNamePattern(Pattern.compile("BLE"))
.build()
// The argument provided in setSingleDevice() determines whether a single
// device name or a list of them appears.
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.setSingleDevice(false)
.build()
// When the app tries to pair with a Bluetooth device, show the
// corresponding dialog box to the user.
deviceManager.associate(pairingRequest,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
startIntentSenderForResult(chooserLauncher,
SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
}
override fun onFailure(error: CharSequence?) {
// Handle the failure.
Log.d("DEVHandler","failed to find dev?")
}
}, null)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
SELECT_DEVICE_REQUEST_CODE -> when(resultCode) {
Activity.RESULT_OK -> {
// The user chose to pair the app with a Bluetooth device.
val deviceToPair: BluetoothDevice? =
data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
Log.d("DEVHandler","try to bond:" + deviceToPair?.name)
deviceToPair?.let { device ->
device.createBond()
val newConn = ConnectThread(deviceToPair).run()
}
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
private inner class ConnectThread(device: BluetoothDevice) : Thread() {
private var mHaveConn = false
public fun IsConnected(): Boolean {
return mHaveConn
}
private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
//device.createRfcommSocketToServiceRecord(ESP_UUID)
device.createInsecureRfcommSocketToServiceRecord(ESP_UUID)
}
public override fun run() {
// Cancel discovery because it otherwise slows down the connection.
BTMan.mBTAdapter?.cancelDiscovery()
mmSocket?.let { socket ->
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
if (socket == null)
Log.d("CONNThread", "Socket is null...")
if (socket.isConnected == true)
Log.d("CONNThread", "Socket is already connected...")
socket.connect()
Log.d("CONNThread", "Made a connection")
// The connection attempt succeeded. Perform work associated with
// the connection in a separate thread.
//manageMyConnectedSocket(socket)
mHaveConn = true
}
}
// Closes the client socket and causes the thread to finish.
fun cancel() {
try {
mmSocket?.close()
} catch (e: IOException) {
Log.e("CONNThread", "Could not close the client socket", e)
}
mHaveConn = false
}
}
I am trying to get my data from Activity to Fragment but couldn't be able to do it ı think there is a problem about requestCodes and resultCodes but o couln't find it. I made a little research but the answers didn't help me.
My companion objects in the fragment
companion object {
const val REQUEST_PERMISSION = 1001
const val REQUEST_MULTIPLE_PERMISSION = 1002
const val REQUEST_TO_POST = 1003
}
Fragment to activity
view.testButton.setOnClickListener {
val intent = Intent (activity, ReaderActivity::class.java)
startActivityForResult(intent,101)
}
Activity to fragment
card_results = result;
Log.d(TAG, "ResultActivity data is :" + result);
Intent returnIntent = new Intent();
returnIntent.putExtra("cardResult", card_results );
setResult(Activity.RESULT_OK, returnIntent);
finish();
Fragment functions to handle data
private fun getDataFromNFC(intent: Intent){
Log.d(TAG, "getDataFromNFC: start")
try {
val result:String = intent.getStringExtra("cardResult").toString()
Log.d(TAG, "getDataFromNFC data is : $result")
}catch (e: NullPointerException){
Log.d(TAG, "error: $e")
Toast.makeText(activity,"Add a card",Toast.LENGTH_SHORT).show()
}
Log.d(TAG, "getDataFromNFC: ends")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Log.d(TAG, "onActivityResult: start")
if (requestCode == 101) {
when (requestCode) {
REQUEST_PERMISSION -> {
if (data != null) {
getDataFromNFC(data)
}
}
REQUEST_MULTIPLE_PERMISSION -> {
// Do something if success / failed
}
REQUEST_TO_POST -> {
// Parse result and do something
}
}
}
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "onActivityResult: ends")
}
The only request code you are using is 101 when you use this code: startActivityForResult(intent,101).
Then in onActivityResult, you first check if the request code is 101, which is fine, but then you have a when(requestCode) block that does stuff only if the request code is not 101, which will never be true for any of those cases, especially inside an if-block that already guaranteed that the request code is 101.
Also, it should be noted that startActivityForResult() is now deprecated in favor of Activity Result APIs.
I am tring to set an image from URL as wallaper for both home screen and lock screen.it is not working for lock screen for API level 28 or lower but works for home screen.
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
var input: InputStream? = null
try {
input = URL(intent).openStream()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val wallpaperManager = WallpaperManager.getInstance(applicationContext)
wallpaperManager.setStream(
input, null, true,
WallpaperManager.FLAG_LOCK or WallpaperManager.FLAG_SYSTEM
)
Toast.makeText(this, "Wallpaper set successfully!", Toast.LENGTH_SHORT)
.show()
}
} catch (e: Exception) {
e.printStackTrace()
}
I am using BiometricPromt in my application.
Now I need to implement way for user to enter the PIN when Biometric is not available or enrolled.
I can use .setDeviceCredentialAllowed(true) but problem with it is when Biometric is not available or enrolled it won't show any dialog, and even if user use PIN if Biometric is available it doesn't registrate it in onAuthenticationSucceeded.
So now I am trying to implement negative button so user can enter PIN and I can catch it and see if user entered it correctly but I can't find anywhere if it is possible to do with default android PIN manager, so I don't have to implement any 3rd party library?
My current code is:
fun biometricCheck(context: Context, fragment: FragmentActivity){
val executor = ContextCompat.getMainExecutor(context)
val biometricManager = BiometricManager.from(context)
init(fragment)
when {
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS -> {
authUser(executor, context, fragment)
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
//No hardware
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
//Hardware unvailable
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
//NONE ENROLLED
}
}
}
private fun init(fragment: FragmentActivity){
//Some code
}
private fun authUser(executor: Executor, context: Context, fragment: FragmentActivity) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Authentication")
//.setSubtitle("Owner confirmation")
.setDescription("Scan fingerprint or enter PIN")
//.setDeviceCredentialAllowed(true)
.setNegativeButtonText("TEST")
.build()
val biometricPrompt = BiometricPrompt(fragment, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
val intent = Intent(context, MainActivity::class.java)
context.startActivity(intent)
}
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
//Error auth
if (errorCode == BiometricConstants.ERROR_USER_CANCELED) {
//Some long code
}
}
if(errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
Log.e(TAG, "User clicked")
//loginWithPassword() // I don't know what to implement here
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
//Failed auth
}
})
biometricPrompt.authenticate(promptInfo)
}