I've encountered a small issue when creating an Alert dialog in a Fragment. The following is the error thrown in the Logcat:
2020-07-26 11:21:47.425 20063-20063/com.example.MyApp E/WindowManager: android.view.WindowLeaked: Activity com.example.MyApp.MainActivity has leaked window DecorView#b9684c5[MainActivity] that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:769)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:440)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:95)
at android.app.Dialog.show(Dialog.java:473)
at com.example.MyApp.ScannerFragment.onCreateView(ScannerFragment.kt:44)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8125)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
I've tried referring to this question which explained that this error may appear possibly due to showing a dialog after an Activity was exited, not calling dismiss() before exiting, or another possibility.
In my context, however, I'm not sure on how to apply the solutions suggested in my context. I'm showing the Alert dialog on a Fragment, ScannerFragment, that is related to an Activity, MainActivity, through a Bottom Navigation menu. The following includes the code for the related files:
ScannerFragment.kt
package com.example.MyApp
import android.Manifest
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.requestPermissions
import androidx.fragment.app.Fragment
class ScannerFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
val builder: AlertDialog.Builder? = this.let {
AlertDialog.Builder(requireActivity())
}
builder
?.setMessage(R.string.camera_unauthorisedMessage)
?.setTitle(R.string.camera_unauthorisedTitle)
?.apply {
setPositiveButton("OK",
DialogInterface.OnClickListener { dialog, _ ->
requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), 1111)
dialog.dismiss()
})
}
builder?.create()?.show() // Line 44 - error occurs here
} else {
Log.e("DB", "PERMISSION GRANTED")
}
return inflater.inflate(R.layout.fragment_scanner, container, false)
}
}
MainActivity.kt
package com.example.MyApp
import android.Manifest
import android.content.DialogInterface
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Sets up the bottom navigation bar for usage
initialiseBottomNav()
}
// Overrides the back button to prevent going back to StartActivity
override fun onBackPressed() {}
private fun initialiseBottomNav() {
val navController = findNavController(R.id.main_navhostfragment)
main_bottomnav.setupWithNavController(navController)
}
}
If I were to refer to the question above, what are the steps that I would need to do to resolve this error? All help is greatly appreciated, thank you!
This circumstance means your activity is finishing or your app is crashing somewhere else in your code, but then the open dialog leads to leak window. A tip can be checking your log (typically lines above the log you have shared) and see if there is any other exception/error happening earlier.
Firstly, I would suggest you to add your AlertDialog code in onViewCreated method of your fragment.
Second, you can add the following checking just before showing your AlertDialog:
if(!requireActivity().isFinishing)
builder?.create()?.show()
This happening because dialog associated with the activity context
use application context
val builder: AlertDialog.Builder? = activity?.applicationContext?.let { AlertDialog.Builder(it) }
if(builder != null) {
builder
.setMessage("")
?.setTitle("R.string.camera_unauthorisedTitle")
?.apply {
setPositiveButton("OK",
DialogInterface.OnClickListener { dialog, _ ->
requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), 1111)
dialog.dismiss()
})
}
builder.create().show() // Line 44 - error occurs here
}
Related
I want to send an editText string from a dialogFragment popup to the activity screen behind it. I'm not sure how to do this in Kotlin since most resources online are in Java. I've tried to implement: How to send data from dialog to my activity kotlin?. However, I couldn't quite get it work so I'm not sure what method to use from here.
Here is the SelectAPScreen.kt (the activity I want the string to go to)
package com.wcsng.dlocapp
import android.annotation.SuppressLint
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.RequiresApi
import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.findNavController
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlinx.android.synthetic.main.imu_data.*
import java.io.BufferedReader
import java.io.InputStreamReader
class SelectAPScreen : AppCompatActivity(), OnButtonClick {
#SuppressLint("ResourceAsColor")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_select_apscreen)
val directoryName = findViewById<EditText>(R.id.dir_field)
val pingIP = findViewById<EditText>(R.id.ip_field)
val rpiIP = findViewById<EditText>(R.id.rpiField)
val qtnIPs = {}
val addQtn = findViewById<Button>(R.id.addQtn)
addQtn.setOnClickListener {
var dialog = AddQtnFragment()
dialog.show(supportFragmentManager, "Add Qtn Popup")
}
}
}
Here is the DialogFragment Code:
package com.wcsng.dlocapp
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.annotation.NonNull
import androidx.fragment.app.DialogFragment
import androidx.navigation.fragment.findNavController
import com.wcsng.dlocapp.R
class AddQtnFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater
val qtn_ip = view?.findViewById<EditText>(R.id.qtn_ip)?.text.toString()
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(inflater.inflate(R.layout.add_qtn_popup, null))
.setPositiveButton("Save", DialogInterface.OnClickListener{dialog, id ->
// TODO Return the qtn_ip to SelectAPScreen
// Close the dialog and return back to the parent activity
dialog.dismiss()
})
.setNegativeButton("Cancel", DialogInterface.OnClickListener{dialog, id ->
dialog.dismiss()
})
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
you can use getActivity() or requireActivity() methods for obtaing managing Activity, then you may cast it to proper class, e.g. (requireActivity() as SelectAPScreen) and now you can call methods of it
(requireActivity() as SelectAPScreen).selectAPScreenMethod()
selectAPScreenMethod() is a custom method placed inside SelectAPScreen class
I followed the steps from the documentation on setting up the email password authentication and it worked fine for a while now. I don't know why but yesterday it just stopped working and keeps giving me the error : W/System: Ignoring header X-Firebase-Locale because its value was null.
The app won't allow me to log in or sign-up anymore.
But the problem only occurs when I test my app while connected via WiFi and it works as expected when I am connected to mobile data (LTE).
I tried connecting to a VPN, in hopes that the problem is with my service provider, but the problem remained.
I've also tried testing the app on a different device, the error is still there.
I've tried following the steps provided by these answers:
https://stackoverflow.com/a/65106158/16296874
https://stackoverflow.com/a/66993601/16296874
https://stackoverflow.com/a/64657110
but nothing seems to be working.
I've seen lots of people encountered this problem and being solved in different ways. And trying those did not seem to have an effect. So I'm not sure anymore.
package com.sunnyside.kookoo.verification.ui.fragments
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.fragment.findNavController
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.sunnyside.kookoo.R
import com.sunnyside.kookoo.databinding.FragmentLoginBinding
import com.sunnyside.kookoo.databinding.FragmentProfileBinding
import com.sunnyside.kookoo.student.ui.StudentActivity
import com.sunnyside.kookoo.testKolanglods.ui.PangTestingLangLodsActivity
class LoginFragment : Fragment() {
private lateinit var auth: FirebaseAuth
private var _binding: FragmentLoginBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentLoginBinding.inflate(inflater, container, false)
val view = binding.root
auth = Firebase.auth
binding.textLoginRegister.setOnClickListener {
findNavController().navigate(R.id.action_loginFragment_to_signupFragment)
}
binding.loginbtnBack.setOnClickListener {
findNavController().navigate(R.id.action_loginFragment_to_welcomeFragment)
}
binding.resetPasswordTxt.setOnClickListener {
findNavController().navigate(R.id.action_loginFragment_to_resetFragment)
}
binding.loginBtn.setOnClickListener {
login()
}
return view
}
private fun login() {
val email = binding.loginEmail.text.toString()
val password = binding.loginPassword.text.toString()
binding.llProgressBar.root.visibility = View.VISIBLE
auth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d("AUTH", "signInWithEmail:success")
val intent = Intent(activity, StudentActivity::class.java)
activity?.startActivity(intent)
} else {
// If sign in fails, display a message to the user.
Log.w("AUTH", "signInWithEmail:failure", task.exception)
Toast.makeText(
context, "Authentication failed.",
Toast.LENGTH_SHORT
).show()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Check date(rules) in Firebase .
I'm trying change some text after load a fragment. It will be location tracker in future. So I'm testing change of the text.
build.gradle module:
android {
buildFeatures{
dataBinding = true
viewBinding = true
}
}
start.xml:
<TextView
android:id="#+id/location"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:text="Current GPS Location"
android:textColor="#FF0000"
android:textSize="30sp"
android:textStyle="bold" />
Start.kt:
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil.setContentView
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.basic.databinding.StartBinding
import android.util.Log
class Start : Fragment() {
private lateinit var binding: StartBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = StartBinding.inflate(layoutInflater)
binding.location.setText("Hello World")
}
}
Application runs without problem but setText Not works. Do you know why?
You are initializing your binding but not adding it to the view
onCreateView(inflater... container...) {
binding = StartBinding.inflate(layoutInflater, container, false)
return binding.root
}
then following the life cycle usage previous to binding, you have to make the change after the view is created
onViewCreated(...) {
binding.location.setText("Hello World")
}
A small comment, your naming doesn't follow conventions:
StartFragment
fragment_start
You should do the binding in onCreateView instead of onCreate as in onCreate() the fragment view is not created yet.
So transfer below in onCreateView
binding = StartBinding.inflate(layoutInflater)
binding.location.setText("Hello World")
For more info check fragment lifecycle
Thanks for answers!
Solution is very simple.
Here is solution for people who will find this question:
class Start : Fragment() {
lateinit var binding : StartBinding //this must be at the begin of your class
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = StartBinding.inflate(layoutInflater,container,false) //this must be here in onCreateView
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textView.setText("Hello World") // and now setText works in onViewCreated
}
}
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.drawerlayout.widget.DrawerLayout
import com.example.tender.R
import com.example.tender.databinding.FragmentAvailableTenderBinding
import com.example.tender.databinding.FragmentLoginBinding
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
/**
* A simple [Fragment] subclass.
*/
class AvailableTenderFragment : Fragment() {
private lateinit var drawerLayout: DrawerLayout
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentAvailableTenderBinding.inflate(inflater)
drawerLayout = binding.drawerLayout
**val navController = this.findNavController(R.id.nav_host_fragment)**
**NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)**
NavigationUI.setupWithNavController(binding.navView, navController)
binding.root
}
}
"To many arguments for public fun Fragment.findNavController():NAvcontroller defined in androidx.navigation.fragment" is showing when i hover on findnavcontroller.I am unable to resolve the error on the above bold lines.In this case what should I do?
As per the Navigate to a destination documentation, the androidx.navigation.fragment.findNavController you've imported takes no parameters (it finds the parent NavHostFragment of the current Fragment and doesn't need the ID of the NavHostFragment).
The lines you've written, namely findNavController(R.id.nav_host_fragment) and setupActionBarWithNavController() are methods you'd call in an Activity, not in a Fragment.
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.widget.*
import android.os.Bundle
import android.R
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val activity_btn = mainbtn
activity_btn.setOnClickListener(View.OnClickListener {
val intent = Intent(this, activity_main2::class.java)
startActivity(intent)
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
})
}
}
I have tried clean project,reopening project,rebuild project etc but none of these have worked.
It says Unresolved reference:activity_main,slide_in_right,slide_out_left etc.
remove line import android.R; from your code and rebuild again