App crashes when getting current user's location (Kotlin) - android

My code compiles but the app will not start now. I've tried many variations to simply get a user's current location and display it using MapsSDK but nothing works.
Fragment Activity:
class BuyFragment : Fragment(), OnMapReadyCallback {
private var _binding: FragmentBuyBinding? = null
private val binding get() = _binding!!
private lateinit var map: GoogleMap
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val locationPermissionCode = 1
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view = inflater.inflate(R.layout.fragment_buy, container, false)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext())
return view
}
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
locationPermissionCode
)
} else {
map.isMyLocationEnabled = true
getCurrentLocation()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == locationPermissionCode) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
map.isMyLocationEnabled = true
getCurrentLocation()
}
}
}
private fun getCurrentLocation() {
fusedLocationClient.lastLocation
.addOnSuccessListener { location: Location? ->
if (location != null) {
val currentLocation = LatLng(location.latitude, location.longitude)
map.addMarker(MarkerOptions().position(currentLocation).title("Current Location"))
map.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 15.0f))
}
}
}
}
Dependencies:
dependencies {
implementation 'androidx.preference:preference:1.0.0-rc02'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
implementation 'com.google.android.gms:play-services-location:11.0.2'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
layout:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.buy.BuyFragment">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
I keep reverting my code to just display maps and that works but I'd like to have the app start on the user's current location.

Could you please share the crash log? That will be helpful to narrow the root issue.
Also, if you haven't already, don't forget to request location permissions so you can use location services.

Related

Kotlin MenuHost Provider not Working onMenuItemSelected

The Menu Host will not trigger with a MenuItem.itemId
I have attemped multiple diffent methods but all to lead nothing working.
The Menu that I have chosen to display shows up but the actions do not work
The object: MenuProvider
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object :MenuProvider{
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.maps_toggle,menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when(menuItem.itemId){
R.id.toggle_group->{
Log.d("FavPlaceOverserMaps1", "it.toString()")
if (isChecked){
favPlacesViewModel.readAllLocations.observe(viewLifecycleOwner, Observer {
Log.d("FavPlaceOverserMaps", it.toString())
})
}
true
}
else->{
false
}
}
}
},viewLifecycleOwner, Lifecycle.State.RESUMED)
}
I have a switch in the menu and the menu does not trigger when toggled
if(menuItem.isChecked == true){
menuItem.isCheck = isChecked
//do something
}
I am unsure if something is wrong as I have moved off the deprecated way of doing a menu handler
Activity Import
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
Dependency,
most dependency are here others include Firebase and Google
dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.5.1"
implementation "androidx.navigation:navigation-ui-ktx:2.5.1"
kapt "com.android.databinding:compiler:3.2.0-alpha10"
//implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.activity:activity-ktx:1.5.1"
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.7.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
}

How to use the new ComponentActivity with ViewBinding and the other old AppCompatActivity components

According to this question, I tried to update my deprecated menus codes like setHasOptionsMenu , onCreateOptionsMenu and onOptionsItemSelected in my fragments and all app, but I should replace AppCompatActivity to ComponentActivity(R.layout.activity_example) but after doing this I see there's some problem, first I confused about how to use ViewBinding with it when I should remove setContentView(binding.root) from activity second the method setSupportActionBar(binding.appBarMain.toolbar) is not found, and I couldn't use the navigation components like supportFragmentManager and setupActionBarWithNavController
the third thing I couldn't"t declare this val menuHost: MenuHost = requireActivity() in onCreateView in fragment I see it's Required:
MenuHost
but Found:
FragmentActivity
here's my MainActivity code before edits
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController
private lateinit var postViewModel: PostViewModel
private lateinit var navGraph: NavGraph
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
postViewModel = ViewModelProvider(this)[PostViewModel::class.java]
postViewModel.getCurrentDestination()
setSupportActionBar(binding.appBarMain.toolbar)
val drawerLayout: DrawerLayout = binding.drawerLayout
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
if (navHostFragment != null) {
navController = navHostFragment.navController
}
navGraph = navController.navInflater.inflate(R.navigation.mobile_navigation)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_accessory,
R.id.nav_arcade, R.id.nav_fashion,
R.id.nav_food, R.id.nav_heath,
R.id.nav_lifestyle, R.id.nav_sports, R.id.nav_favorites, R.id.about
), drawerLayout
)
// setupActionBarWithNavController(navController, appBarConfiguration)
// navView.setupWithNavController(navController)
setupActionBarWithNavController(this, navController, appBarConfiguration)
setupWithNavController(binding.navView, navController)
// determineAdvertisingInfo()
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
and this the implementation of menus in fragments
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
setHasOptionsMenu(true)
return binding.root
}
....................................................................................
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.main, menu)
super.onCreateOptionsMenu(menu, inflater)
val searchManager =
requireContext().getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchView = menu.findItem(R.id.app_bar_search).actionView as SearchView
searchView.setSearchableInfo(searchManager.getSearchableInfo(requireActivity().componentName))
searchView.queryHint = resources.getString(R.string.searchForPosts)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(keyword: String): Boolean {
if (keyword.isEmpty()) {
Snackbar.make(
requireView(),
"please enter keyword to search",
Snackbar.LENGTH_SHORT
).show()
}
// itemArrayList.clear()
if (Utils.hasInternetConnection(requireContext())) {
postViewModel.getItemsBySearch(keyword)
postViewModel.searchedPostsResponse.observe(viewLifecycleOwner) { response ->
when (response) {
is NetworkResult.Success -> {
hideShimmerEffect()
itemArrayList.clear()
binding.progressBar.visibility = View.GONE
response.data?.let {
itemArrayList.addAll(it.items)
}
adapter.notifyDataSetChanged()
}
is NetworkResult.Error -> {
hideShimmerEffect()
// loadDataFromCache()
Toast.makeText(
requireContext(),
response.toString(),
Toast.LENGTH_LONG
).show()
}
is NetworkResult.Loading -> {
if (postViewModel.recyclerViewLayout.value == "titleLayout" ||
postViewModel.recyclerViewLayout.value == "gridLayout"
) {
hideShimmerEffect()
} else {
showShimmerEffect()
}
}
}
}
} else {
postViewModel.getItemsBySearchInDB(keyword)
postViewModel.postsBySearchInDB.observe(viewLifecycleOwner) { items ->
if (items.isNotEmpty()) {
hideShimmerEffect()
binding.progressBar.visibility = View.GONE
itemArrayList.clear()
itemArrayList.addAll(items)
adapter.notifyDataSetChanged()
}
}
}
return false
}
override fun onQueryTextChange(newText: String): Boolean {
return false
}
})
searchView.setOnCloseListener {
if (Utils.hasInternetConnection(requireContext())) {
Log.d(TAG, "setOnCloseListener: called")
itemArrayList.clear()
requestApiData()
} else {
noInternetConnectionLayout()
}
false
}
postViewModel.searchError.observe(viewLifecycleOwner) { searchError ->
if (searchError) {
Toast.makeText(
requireContext(),
"There's no posts with this keyword", Toast.LENGTH_LONG
).show()
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.change_layout) {
changeAndSaveLayout()
return true
}
return super.onOptionsItemSelected(item)
}
build.gradle dependencies
dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation ('com.google.android.material:material:1.6.1') {
exclude(group: 'androidx.recyclerview', module: 'recyclerview')
exclude(group: 'androidx.recyclerview', module: 'recyclerview-selection')
}
implementation "androidx.recyclerview:recyclerview:1.2.1"
// For control over item selection of both touch and mouse driven selection
implementation "androidx.recyclerview:recyclerview-selection:1.1.0"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// //Moshi
// implementation("com.squareup.moshi:moshi:1.13.0")
// implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
// kapt "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'org.jsoup:jsoup:1.14.1'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'org.apache.commons:commons-lang3:3.8.1'
implementation 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
implementation "androidx.browser:browser:1.4.0"
implementation 'androidx.multidex:multidex:2.0.1'
configurations {
all*.exclude group: 'com.google.guava', module: 'listenablefuture'
}
//Room
implementation "androidx.room:room-runtime:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.room:room-ktx:2.4.2"
androidTestImplementation "androidx.room:room-testing:2.4.2"
//Dagger - Hilt
implementation 'com.google.dagger:hilt-android:2.42'
kapt 'com.google.dagger:hilt-android-compiler:2.42'
//SDP & SSP
implementation 'com.intuit.sdp:sdp-android:1.0.6'
implementation 'com.intuit.ssp:ssp-android:1.0.6'
// Shimmer
implementation 'com.facebook.shimmer:shimmer:0.5.0'
//firebase & analytics
implementation platform('com.google.firebase:firebase-bom:28.4.0')
implementation 'com.google.firebase:firebase-analytics'
//crashlytics
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
// DataStore
implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation("androidx.datastore:datastore-preferences-rxjava3:1.0.0")
//admob
implementation 'com.google.android.gms:play-services-ads:21.1.0'
implementation platform('com.google.firebase:firebase-bom:30.2.0')
implementation project(':nativetemplates')
implementation("androidx.ads:ads-identifier:1.0.0-alpha04")
// Used for the calls to addCallback() in the snippets on this page.
implementation("com.google.guava:guava:28.0-android")
implementation 'com.google.firebase:firebase-analytics'
implementation("androidx.activity:activity-ktx:1.5.0")
}
Simplest solution from top of my head (hopefully it will work for you):
val menuHost: MenuHost = requireActivity() as MenuHost
This example is for fragment but I guess you can easily apply it to activity:
First extend fragment with MenuProvider:
class SomeFragment : SomethingIfYouHave(), MenuProvider { /* Your code */ }
Now in onCreateView:
val menuHost: MenuHost = requireActivity() as MenuHost
menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
Later in code override onCreateMenu and onMenuItemSelected.
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.info_menu, menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.something-> {
// Do something here
true
}
else -> false
}
}
AppCompatActivity extends FragmentActivity which extends ComponentActivity, why to use ComponentActivity directly?
Please revert it to:
class MainActivity : AppCompatActivity() { /* Your code here */ }
I think your only problem was in this line:
val menuHost: MenuHost = requireActivity() as MenuHost
------------------- UPDATE
*ComponentActivity has all you need for a Compose-only app.
If you need AppCompat APIs, an AndroidView which works with AppCompat or MaterialComponents theme, or you need Fragments then use AppCompatActivity.
Reference*
You are not using purely ComposeUI, so that is why you need AppCompat and not ComponentActivity.
If you want to use ComponentActivity without AppCompat you need to get rid off supportFragmentManager.
This would go out of the scope but please check ComposeUI Fragments if you want to switch on ComposeUI (I didn't write article).

Recycler View doesn't update list after notifyDataSetChanged()

to describe the problem simply, i have main activity where it has viewPager2 which displays 2 fragments. The main activity also obtains a toolbar with search MenuOptions. i am trying to search artist therefore it will automatically update the recycler view in the first fragment (videoCallHomefragment). i don't know where is the problem as this is my 3rd day in debugging this matter and i still ended up nowhere.
my assumptions
there must be conflict because i am using viewpager2 and trying to update recycler view in a fragment from an activity
dependencies versioning conflict that results in "notifyDataSetChanged()" not working, on the other hand, i tried an alternative using DiffUtil and still not working
p.s. the app successfully retrieves artists and displays them in recycler view which indicated that the function "setData" in the adapter is working, but when searching for artist, the "onBindViewHolder" and "onCreateViewHolder" doesn't get initiated or called when calling notifyDataSetChanged()
See Below code snippets
MainActivity
#AndroidEntryPoint
class MainActivity : AppCompatActivity(), ArtistsAdapter.OnArtistListener {
private lateinit var binding: ActivityMainBinding
private val mAdapter by lazy { ArtistsAdapter(this) }
private val mainViewModel: MainViewModel by viewModels()
private lateinit var viewPager2:ViewPager2
private lateinit var fragement: Fragment
private lateinit var artists: List<Artist>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
fragement = supportFragmentManager.findFragmentById(R.id.videoCallHomeFragment)!!
setSupportActionBar(findViewById(R.id.toolbar))
val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
viewPager2 = findViewById(R.id.view_pager_2)
val adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
viewPager2.adapter = adapter
TabLayoutMediator(tabLayout, viewPager2){tab, postion ->
when(postion) {
0 -> {
tab.text="Artists"
}
1 -> {
tab.text = "Voice Calls"
}
}
}.attach()
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback(), SearchView.OnQueryTextListener {
#SuppressLint("ResourceType")
override fun onPageSelected(position: Int) {
if(position == 0)
{
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.inflateMenu(R.menu.search_menu)
val search = toolbar.menu.findItem(R.id.menu_search)
val searchView = search?.actionView as? SearchView
searchView?.isSubmitButtonEnabled = true
searchView?.setOnQueryTextListener(this)
} else {
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.menu.clear()
}
}
#SuppressLint("NotifyDataSetChanged")
override fun onQueryTextSubmit(query: String?): Boolean {
if(query != null){
// recyclerView.adapter = mAdapter
// recyclerView.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
searchArtist(query)
mAdapter.notifyDataSetChanged()
}
return true
}
#SuppressLint("NotifyDataSetChanged")
override fun onQueryTextChange(newText: String?): Boolean {
if(newText != null){
// recyclerView.adapter = mAdapter
// recyclerView.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
searchArtist(newText)
mAdapter.notifyDataSetChanged()
}
return true
}
})
requestPermissions()
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.setBackgroundColor(Color.rgb(32, 4, 209))
toolbar.setTitleTextColor(Color.WHITE)
toolbar.setTitle(R.string.app_name)
}
private fun requestPermissions()
{
if (PermissionRequestUtil.hasCameraPermissions(this)) {
return
} else {
EasyPermissions.requestPermissions(
this,
"Please accept camera permissions to use this app.",
0,
Manifest.permission.CAMERA
)
}
}
#SuppressLint("NotifyDataSetChanged")
private fun searchArtist(query: String)
{
val searchQuery = "%$query%"
lifecycle.coroutineScope.launch {
mainViewModel.searchArtist(searchQuery).collect { it ->
mAdapter.setData(it)
mAdapter.notifyDataSetChanged()
Log.i(TAG, "new artist List set!!")
}
}
}
override fun onArtistClick(artist: Artist, position: Int) {
TODO("Not yet implemented")
}
}
VideoCallHomeFragment
package com.example.awfc.ui
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.coroutineScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.awfc.R
import com.example.awfc.adapters.ArtistsAdapter
import com.example.awfc.data.Artist
import com.example.awfc.viewmodels.MainViewModel
import com.todkars.shimmer.ShimmerRecyclerView
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
#AndroidEntryPoint
class VideoCallHomeFragment : Fragment() , ArtistsAdapter.OnArtistListener {
private lateinit var mainViewModel: MainViewModel
private val mAdapter by lazy { ArtistsAdapter(this)}
private lateinit var mView: View
private lateinit var artists: List<Artist>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
mView = inflater.inflate(R.layout.fragment_video_call_home, container, false)
setHasOptionsMenu(true)
//val shimmer = mView.findViewById<ShimmerRecyclerView>(R.id.recycler_view)
//shimmer.showShimmer()
setupRecyclerView()
mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
lifecycle.coroutineScope.launch {
mainViewModel.getArtists().collect {
artists = it
mAdapter.setData(it)
//shimmer.hideShimmer()
}
}
return mView
}
#SuppressLint("CutPasteId")
fun setupRecyclerView()
{
mView.findViewById<RecyclerView>(R.id.recycler_view).adapter = mAdapter
mView.findViewById<RecyclerView>(R.id.recycler_view).layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
}
override fun onArtistClick(artist: Artist, position: Int) {
artists[position]
val intent = Intent(this.context, ArtistDetailsActivity::class.java)
intent.putExtra("artistName", artist.name)
intent.putExtra("arabicName", artist.name_arabic)
intent.putExtra("arabicDesc", artist.description_arabic)
intent.putExtra("artistDesc", artist.description)
intent.putExtra("artistImage", artist.image)
intent.putExtra("artistVideo1", artist.videoUrl1)
intent.putExtra("artistVideo2", artist.videoUrl2)
intent.putExtra("artistVideo3", artist.videoUrl3)
startActivity(intent)
}
}
Artist Adapter
package com.example.awfc.adapters
import android.annotation.SuppressLint
import android.content.ContentValues.TAG
import android.content.Intent
import android.service.autofill.OnClickAction
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.awfc.data.Artist
import com.example.awfc.databinding.ArtistRowLayoutBinding
import com.example.awfc.utils.ArtistsDiffUtil
class ArtistsAdapter(var artistListener: OnArtistListener) : RecyclerView.Adapter<ArtistsAdapter.MyViewHolder>() {
private var artists = emptyList<Artist>()
class MyViewHolder(private val binding: ArtistRowLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun init(artist: Artist, action: OnArtistListener)
{
itemView.setOnClickListener {
action.onArtistClick(artist, adapterPosition)
}
}
fun bind(modelClass: Artist) {
binding.result = modelClass
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ArtistRowLayoutBinding.inflate(layoutInflater, parent, false)
return MyViewHolder(binding)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
Log.i(TAG, "OnCreateViewHolder initiated")
return MyViewHolder(
ArtistRowLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentResult = artists[position]
holder.bind(currentResult)
holder.init(currentResult, artistListener)
Log.i(TAG, "OnBindViewHOlder initiated")
}
override fun getItemCount(): Int {
return artists.size
}
#SuppressLint("NotifyDataSetChanged")
fun setData(newData: List<Artist>) {
val artistsDiffUtil = ArtistsDiffUtil(artists, newData)
val diffUtilResult = DiffUtil.calculateDiff(artistsDiffUtil)
artists = emptyList()
artists = newData
diffUtilResult.dispatchUpdatesTo(this)
this.notifyDataSetChanged()
//this.notifyDataSetChanged()
}
interface OnArtistListener {
fun onArtistClick(artist:Artist, position: Int)
}
}
artistDiffUtil
package com.example.awfc.utils
import androidx.recyclerview.widget.DiffUtil
class ArtistsDiffUtil<T>(
private val oldList: List<T>,
private val newList: List<T>
): DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] === newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
gradleFile
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'kotlin-parcelize'
id 'androidx.navigation.safeargs'
}
android {
compileSdk 31
buildFeatures {
viewBinding true
dataBinding true
}
defaultConfig {
applicationId "com.example.awfc"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments += ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "com.google.dagger:hilt-android:2.28.3-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28.3-alpha"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation 'com.todkars:shimmer-recyclerview:0.4.1'
implementation 'de.hdodenhof:circleimageview:3.1.0'
// Image Loading library Coil
implementation "io.coil-kt:coil:0.13.0"
implementation 'com.squareup.picasso:picasso:2.71828'
// UI Tests
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
// When using a AppCompat theme
implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0"
implementation "com.google.android.material:compose-theme-adapter:1.1.1"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation 'com.yqritc:android-scalablevideoview:1.0.4'
implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation("javax.inject:javax.inject:1")
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// viewpager2
implementation 'androidx.viewpager2:viewpager2:1.0.0'
//tablayout
implementation 'com.google.android.material:material:1.3.0-alpha04'
// For developers using AndroidX in their applications
implementation 'pub.devrel:easypermissions:3.0.0'
}
You're creating two separate instances of ArtistsAdapter in MainActivity and VideoCallHomeFragment. It sounds like the adapter works fine if setData is working inside the fragment that's actually displaying the RecyclerView, because that's the one that has access to the actual adapter that's displaying the data.
But in searchArtist in MainActivity, you're calling setData on a completely different adapter instance that isn't connected to the RecyclerView in any way, so nothing's gonna happen.
Instead of having the activity trying to talk to a widget hosted in a fragment, it would be better to have a LiveData or similar in your view model that contains the data that's supposed to be displayed. Make VideoCallHomeFragment observe that, and it can call setData on the adapter. Your activity can call viewModel.getArtists() or whatever, but that function should internally update the LiveData so that anything observing it will see the new data to display.

How to fix 'Cannot create an instance of class....'?

Here is my build.gradle
dependencies {
//viewmodel
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
//noinspection GradleDependency
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
}
viewModel
class AlarmViewModel(application: Application) : AndroidViewModel(application) {
private var repository : AlarmRepository = AlarmRepository(application)
var list : LiveData<List<Alarm>> = repository.list
}
Fragment
class AlarmListFragment : Fragment() {
private lateinit var viewModel: AlarmViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAlarmListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(activity!!.application)).get(AlarmViewModel::class.java)
}
}
}
Searching on google and trying to fix but it doesn't work
plz help me :<
I need ur helpe!!!

Android: GoogleMap callbacks not being called

I've started a new Android project based around a Google map using the Android Studio template, and my callbacks are not getting called at all.
Here is my activity code:
class MapsActivity : BaseActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
val center = LatLng(/*Redacted*/)
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 18.0F))
mMap.setOnCameraChangeListener { GoogleMap.OnCameraChangeListener {
val bounds = mMap.projection.visibleRegion.latLngBounds
} }
mMap.setOnMapClickListener { GoogleMap.OnMapClickListener {
val lat = it.latitude
} }
mMap.setOnCameraIdleListener { GoogleMap.OnCameraIdleListener {
val bounds = mMap.projection.visibleRegion.latLngBounds
} }
mMap.setOnCameraMoveListener { GoogleMap.OnCameraMoveListener {
val bounds = mMap.projection.visibleRegion.latLngBounds
} }
}
}
The contents of the callbacks aren't important at the moment, I'm setting breakpoints within them all and none of them are getting called.
Here's my app gradle file:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "redacted"
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.google.android.gms:play-services-maps:15.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}
This might be a red herring, but occasionally I get an error in the console and the map stops working at all:
A/art: art/runtime/stack.cc:153] Check failed: success Failed to read the this object in void com.google.maps.api.android.lib6.gmm6.vector.gl.drawable.y.a(com.google.maps.api.android.lib6.gmm6.vector.gl.f, com.google.maps.api.android.lib6.gmm6.vector.camera.c, com.google.maps.api.android.lib6.gmm6.vector.m)
What am I doing wrong?
Edit:
Here's my manifest file as requested:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="redacted">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key"/>
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Edit 2:
Here's my layout file:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/map"
tools:context=".MapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
In your code the above breakpoint will hit.
Try this. In order for the function to execute you need to go by one of the following approaches.
class MapsActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnCameraMoveListener, GoogleMap.OnMapClickListener {
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
val center = LatLng(/*Redacted*/)
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 18.0F))
mMap.setOnCameraMoveListener(this)
mMap.setOnMapClickListener(this)
}
override fun onCameraMove() {
val i = 0
val bounds = mMap.projection.visibleRegion.latLngBounds
}
override fun onMapClick(p0: LatLng?) {
val i = 0
val bounds = mMap.projection.visibleRegion.latLngBounds
}
}
Or another way to set listeners is as follows
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
val center = LatLng(/*Redacted*/)
mMap.setOnCameraMoveListener {
val bounds = mMap.projection.visibleRegion.latLngBounds
}
mMap.setOnMapClickListener {
val bounds = mMap.projection.visibleRegion.latLngBounds
}
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 18.0F))
}
you have to initialize google api client in onCreate() method.
mGoogleApiClient = GoogleApiClient.Builder(this)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.enableAutoManage(this, this)
.build()

Categories

Resources