My app has a searchview in its top appbar. I am getting suggestions from the PLaces SDK autocomplete feature but when I click any of the suggestions my app crashes and I get this error:
FATAL EXCEPTION: main
Process: app.vibecast, PID: 6620
java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.Cursor androidx.cursoradapter.widget.CursorAdapter.getCursor()' on a null object reference
at androidx.appcompat.widget.SearchView.launchSuggestion(SearchView.java:1494)
at androidx.appcompat.widget.SearchView.onItemClicked(SearchView.java:1407)
at androidx.appcompat.widget.SearchView$8.onItemClick(SearchView.java:1432)
at android.widget.AutoCompleteTextView.performCompletion(AutoCompleteTextView.java:1071)
at android.widget.AutoCompleteTextView.access$600(AutoCompleteTextView.java:101)
android.widget.AutoCompleteTextView$DropDownItemClickListener.onItemClick(AutoCompleteTextView.java:1419)
The appbar is generated by the Theme.MaterialComponents.DayNight.DarkActionBar theme using this menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_save_location"
android:icon="@drawable/save_location_icon"
android:title="@string/save_location"
android:checkable="true"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_save_image"
android:icon="@drawable/favorite_unselected"
android:title="@string/save_image_btn"
android:checkable="true"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_search"
android:title="@string/search_bar_text"
app:showAsAction="ifRoom"
app:actionViewClass="androidx.appcompat.widget.SearchView"
/>
</menu>
This is the activity setting all of this up:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var searchView: SearchView
private lateinit var suggestionsAdapter: ArrayAdapter<String>
private val currentLocationViewModel : CurrentLocationViewModel by viewModels()
private val accountViewModel : AccountViewModel by viewModels()
private lateinit var imageList : List<ImageDto>
private lateinit var currentImage : ImageDto
private lateinit var currentLocation : LocationDto
private var isCurrentLocationFragmentVisible: Boolean = true
private lateinit var placesClient: PlacesClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_content_home) as NavHostFragment
val navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home,
R.id.nav_account, R.id.nav_pictures, R.id.nav_settings
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
currentLocationViewModel.galleryImages.observe(this){
imageList = it
}
currentLocationViewModel.image.observe(this){
currentImage = it
}
currentLocationViewModel.weather.observe(this){
currentLocation = LocationDto(it.location.cityName, it.location.locationIndex)
}
NavigationUI.setupWithNavController(binding.navView, navController)
navController.addOnDestinationChangedListener { _, destination, _ ->
isCurrentLocationFragmentVisible = destination.id == R.id.nav_home
if (isCurrentLocationFragmentVisible) {
invalidateOptionsMenu()
}
}
Places.initialize(applicationContext, BuildConfig.MAPS_KEY)
placesClient = Places.createClient(this)
suggestionsAdapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main, menu)
val searchItem = menu.findItem(R.id.action_search)
searchView = searchItem.actionView as SearchView
val autoCompleteTextViewId = androidx.appcompat.R.id.search_src_text
val autoCompleteTextView =
searchView.findViewById<AutoCompleteTextView>(autoCompleteTextViewId)
autoCompleteTextView.setAdapter(suggestionsAdapter)
// autoCompleteTextView.post {
// autoCompleteTextView.setAdapter(suggestionsAdapter)
// }
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return true // Return true to indicate that query change has been handled
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText != null) {
fetchCitySuggestions(newText)
}
return true
}
})
return true
}
private fun fetchCitySuggestions(query: String) {
val autocompleteRequest = AutocompleteSessionToken.newInstance()
val request = FindAutocompletePredictionsRequest.builder()
.setTypesFilter(listOf(PlaceTypes.CITIES))
.setSessionToken(autocompleteRequest)
.setQuery(query)
.build()
placesClient.findAutocompletePredictions(request)
.addOnSuccessListener { response ->
val suggestions = response.autocompletePredictions.map { it.getFullText(null).toString() }
updateCitySuggestions(suggestions)
}
.addOnFailureListener { exception ->
Log.e("PlacesSDK", "Error fetching city suggestions: ${exception.localizedMessage}")
}
}
private fun updateCitySuggestions(suggestions: List<String>) {
suggestionsAdapter.clear()
suggestionsAdapter.addAll(suggestions)
suggestionsAdapter.notifyDataSetChanged()
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
val saveImageItem = menu.findItem(R.id.action_save_image)
if (isCurrentLocationFragmentVisible) {
saveImageItem?.icon = ContextCompat.getDrawable(this, R.drawable.favorite_unselected)
} else {
saveImageItem?.icon = ContextCompat.getDrawable(this, R.drawable.favorite_selected)
}
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_save_image -> {
if (item.isCheckable) {
if (!item.isChecked) {
item.isChecked = true
currentLocationViewModel.addImage(currentImage)
item.icon = ContextCompat.getDrawable(this, R.drawable.favorite_selected)
} else {
item.isChecked = false
currentLocationViewModel.deleteImage(currentImage)
item.icon = ContextCompat.getDrawable(this, R.drawable.favorite_unselected)
}
}
true
}
R.id.action_save_location -> {
if (item.isCheckable) {
if (!item.isChecked) {
item.isChecked = true
accountViewModel.addLocation(currentLocationViewModel.weather.value!!.location)
item.icon = ContextCompat.getDrawable(this, R.drawable.delete_location_icon)
} else {
item.isChecked = false
accountViewModel.deleteLocation(currentLocation)
item.icon = ContextCompat.getDrawable(this, R.drawable.save_location_icon)
}
}
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_home)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}