I am developing an app, where users can add their address in their profile and now I want to add a viewModel observer on the coordinates I am receiving from the profile fragment, like my fragment A and give this information to a map fragment, so I can create a new marker on that location. Apparently I am not getting any information of the observer in my fragment B.
So that is my code:
This is the first fragment of the profile:
class ProfilFoto : Fragment() {
lateinit var mDatabase : DatabaseReference
var mAuth = FirebaseAuth.getInstance()
var user = FirebaseAuth.getInstance().currentUser
var DISPLAY_NAME : String? = null
private var model: Communicator?=null
#SuppressLint("SetTextI18n", "ResourceType")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// setContentView(R.layout.fragment_notifications)
val rootView: View = inflater.inflate(R.layout.fragment_profil_foto, container, false)
model= activity?.let { ViewModelProviders.of(it).get(Communicator::class.java) }
val loca = rootView.findViewById<View>(R.id.locationtext) as TextView
var uid = user!!.uid
val userHashMap = HashMap<String, Any>()
mDatabase = FirebaseDatabase.getInstance().getReference("User").child(uid)
val speicherButton = rootView.findViewById<Button>(R.id.speichern)
speicherButton.setOnClickListener {
val location = rootView.findViewById<View>(R.id.locationPerson) as EditText
val loc = location.text.toString()
if (!loc.isEmpty()) {
Log.e("Location", loc)
val locationAddress = GeocodingLocation()
locationAddress.getAddressFromLocation(loc,
getActivity()?.getApplicationContext(), GeocoderHandler())
model!!.setMsgCommunicator(locationAddress.toString())
//userHashMap["Adresse"] = loc
mDatabase.child("Adresse").setValue(loc)
.addOnSuccessListener {
Log.e("Telefon", loc)
}
.addOnFailureListener {
Log.e("Telefon2", "failed")
}
// mDatabase.updateChildren(userHashMap)
}
}
mDatabase.addValueEventListener(object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
return rootView
}
private class GeocoderHandler : Handler() {
override fun handleMessage(message: Message) {
val locationAddress: String?
locationAddress = when (message.what) {
1 -> {
val bundle: Bundle = message.getData()
bundle.getString("address")
}
else -> null
}
if (locationAddress != null) {
Log.e("Handler", locationAddress)
}
}
}
}
And this is the map fragment where the message should appear:
class MapsFragment : Fragment() {
private val callback = OnMapReadyCallback { googleMap ->
val model= activity?.let { ViewModelProviders.of(it).get(Communicator::class.java) }
if (model != null) {
model.message.observe(this, { t ->
//txt.text = o!!.toString()
Log.e("Marker", t.toString())
var latlong = t.toString().split(",")
var lat = parseFloat(latlong[0])
var long = parseFloat(latlong[1])
googleMap.addMarker(
MarkerOptions()
.position(LatLng(lat.toDouble(), long.toDouble()))
.title("Der Neue")
)
})
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView : View = inflater.inflate(R.layout.fragment_maps, container, false)
...
return rootView
}
and this is the communicator class I am using:
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class Communicator : ViewModel(){
val message =MutableLiveData<Any>()
fun setMsgCommunicator(msg:String){
message.setValue(msg)
}
}
and maybe it is important to also show the GeoLocator from where I am getting the coordinates:
private const val TAG = "GeocodingLocation"
public class GeocodingLocation {
lateinit var mDatabase : DatabaseReference
var user = FirebaseAuth.getInstance().currentUser
fun getAddressFromLocation(locationAddress: String,
context: Context?, handler: Handler?) {
val thread: Thread = object : Thread() {
override fun run() {
val geocoder = Geocoder(context, Locale.getDefault())
var result: String? = null
var uid = user!!.uid
mDatabase = FirebaseDatabase.getInstance().getReference("User").child(uid)
try {
val addressList: List<*>? = geocoder.getFromLocationName(locationAddress, 1)
if (addressList != null && addressList.size > 0) {
val address: Address = addressList[0] as Address
val sb = StringBuilder()
sb.append(address.getLatitude()).append("\n")
mDatabase.child("Latitude").setValue(address.latitude)
.addOnSuccessListener {
Log.e("Telefon", address.latitude.toString())
}
.addOnFailureListener {
Log.e("Telefon2", "failed")
}
sb.append(address.getLongitude()).append("\n")
mDatabase.child("Longtitude").setValue(address.longitude)
.addOnSuccessListener {
Log.e("Telefon", address.longitude.toString())
}
.addOnFailureListener {
Log.e("Telefon2", "failed")
}
result = sb.toString()
}
} catch (e: IOException) {
Log.e(TAG, "Unable to connect to Geocoder", e)
} finally {
val message: Message = Message.obtain()
message.setTarget(handler)
if (result != null) {
message.what = 1
val bundle = Bundle()
bundle.putString("address", result)
message.setData(bundle)
} else {
message.what = 1
val bundle = Bundle()
result = """Address: $locationAddress
Unable to get Latitude and Longitude for this address location."""
bundle.putString("address", result)
message.setData(bundle)
}
message.sendToTarget()
}
}
}
thread.start()
}
}
Does anybody know what I am doing wrong to get absolutely nothing? Like in the profile fragment I am receiving a latitude and longitude in the Log.
Move the registering observer code, outside the callback block. Inside the callback, set the value when map is ready to update. In the observer check if map is ready, set the marker. Hope that works!
private lateinit var googleMap: GoogleMap
private val callback = OnMapReadyCallback { map ->
googleMap = map
}
val model= activity?.let { ViewModelProviders.of(it).get(Communicator::class.java) }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView : View = inflater.inflate(R.layout.fragment_maps, container, false)
if (model != null) {
model.message.observe(this, { t ->
//txt.text = o!!.toString()
Log.e("Marker", t.toString())
var latlong = t.toString().split(",")
var lat = parseFloat(latlong[0])
var long = parseFloat(latlong[1])
googleMap?.let{
it.addMarker(
MarkerOptions()
.position(LatLng(lat.toDouble(), long.toDouble()))
.title("Der Neue")
)
})
}
}
return rootView
}
Related
I tried to pass data from DetailActivity to PaymentActivity, but I got this error
Here is the error and here is PaymentFragment after I comment textview that contain number format on it here it is. The user data pass successfully, but the product data unsuccess to pass I think. So how to pass the product data from DetailActivity to PaymentActivity
here is my code:
Helpers File
object Helpers {
fun getDefaultGson(): Gson {
return GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setDateFormat(Cons.DATE_FORMAT_SERVER)
.registerTypeAdapter(Date::class.java, JsonDeserializer { json, _, _ ->
val formatServer = SimpleDateFormat(Cons.DATE_FORMAT_SERVER, Locale.ENGLISH)
formatServer.timeZone = TimeZone.getTimeZone("UTC")
formatServer.parse(json.asString)
})
.registerTypeAdapter(Date::class.java, JsonSerializer<Date> { src, _, _ ->
val format = SimpleDateFormat(Cons.DATE_FORMAT_SERVER, Locale.ENGLISH)
format.timeZone = TimeZone.getTimeZone("UTC")
if (src != null) {
JsonPrimitive(format.format(src))
} else {
null
}
})
.create()
}
fun Throwable.getErrorBodyMessage(): String {
return if (this is HttpException) {
val errorCode = this.code()
if (errorCode == 405) {
"Method yang digunakan salah"
} else if (errorCode == 503) {
"Error Server"
} else {
val parseErrorBody = this.response()?.errorBody()!!.parseErrorBody()
if (parseErrorBody?.meta?.message == null) {
"Permintaan anda belum berhasil di proses. Silakan coba kembali"
} else {
parseErrorBody?.meta?.message.toString()
}
}
} else if (this is ConnectException || this is UnknownHostException) {
"Maaf Anda sedang Offline. Silakan coba kembali"
} else {
return if (this.message == null)
"Permintaan anda belum berhasil di proses. Silakan coba kembali"
else if (this.message.equals(""))
""
else
this.message!!
}
}
fun ResponseBody.parseErrorBody(): Wrapper<*>? {
val gson = Gson()
val adapter = gson.getAdapter(Wrapper::class.java)
try {
return adapter.fromJson(string())
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
fun TextView.formatPrice(value: String) {
this.text = getCurrencyIdr(java.lang.Double.parseDouble(value))
}
fun getCurrencyIdr(price: Double): String {
val format = DecimalFormat("#,###,###")
return "Rp. " + format.format(price).replace(",".toRegex(), ".")
}
fun Long.convertLongToTime(formatTanggal: String): String {
val date = Date(this)
val format = SimpleDateFormat(formatTanggal)
return format.format(date)
}
}
PaymentFragment:
class PaymentFragment : Fragment(), PaymentContract.View {
var progressDialog: Dialog? = null
var total : Int = 0
lateinit var presenter: PaymentPresenter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_payment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as DetailActivity?)!!.toolbarPayment()
var data = arguments?.getParcelable<Data>("data")
initView(data)
initView()
presenter = PaymentPresenter(this)
}
private fun initView(data: Data?) {
tvTitle.text = data?.name
tvPrice.formatPrice(data?.price.toString())
Glide.with(requireContext())
.load(data?.picturePath)
.into(tvPoster)
tvNameItem.text = data?.name
tvHarga.formatPrice(data?.price.toString())
if (!data?.price.toString().isNullOrEmpty()) {
var totalTax = data?.price?.div(10)
tvTax.formatPrice(totalTax.toString())
total = data?.price!! + totalTax!! + 50000
tvTotalPrice.formatPrice(total.toString())
} else {
tvPrice.text = "IDR. 0"
tvTax.text = "IDR. 0"
tvTotalPrice.text = "IDR. 0"
total = 0
}
var user = IcaCraft.getApp().getUser()
var userResponse = Gson().fromJson(user, User::class.java)
tvNameDeliver.text = userResponse?.name
tvPhoneDeliver.text = userResponse?.phoneNumber
tvAddressDeliver.text = userResponse?.address
tvHouseNo.text = userResponse?.houseNumber
tvCityDeliver.text = userResponse?.postalCode
btn_CheckoutNow.setOnClickListener {
presenter.getCheckout(
data?.id.toString(),
userResponse?.id.toString(),
"1",
total.toString(), it
)
}
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
}
override fun onCheckoutSuccess(checkoutResponse: CheckoutResponse, view: View) {
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(checkoutResponse.paymentUrl)
startActivity(i)
Navigation.findNavController(view).navigate(R.id.action_fragmentPayment_to_fragmentPaymentSuccess)
}
override fun onCheckoutFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
DetailActivity:
class DetailFragment : Fragment() {
var data:Data?= null
var bundle:Bundle?= null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_detail, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as DetailActivity?)!!.toolbarDetail()
arguments?.let {
DetailFragmentArgs.fromBundle(it).data?.let {
initView(it)
}
}
btnOrderNow.setOnClickListener { view ->
Navigation.findNavController(view).navigate(R.id.action_fragmentDetail_to_fragmentPayment, bundle)
}
}
private fun initView(data: Data?) {
data?.let {
// bundle = bundleOf("data" to data)
Glide.with(requireContext())
.load(it.picturePath)
.into(ivPoster)
tvTitle.text = it.name
tvDescription.text = it.description
tvMaterials.text = it.materials
tvTotal.formatPrice(it.price.toString())
}
}
}
I am not sure about the bug in the code but there are many other ways you can share data in between activities. Try using viewModel to share data between your activities. SharedPreferences are another way too. There are plenty of other ways but I think viewModel is the best way. I personally find it difficult to transfer data between fragments using arguments. So Good Luck!
Your code is a bit complicated but the error is understandable. It says "You can not parse null to double". You define "price" as "Int?", so it can be "null" and when it is null, app crashes. This can be solved by doing the followings:
1- You can define your price "not null" and set a default value as 0.
#Expose
#SerializedName("price")
val price: Int = 0
2- If you do not want to change your data class, you need to control what to pass to "parseDouble()" method.
fun TextView.formatPrice(value: String) {
if (value != null) {
this.text = getCurrencyIdr(java.lang.Double.parseDouble(value))
} else {
this.text = getCurrencyIdr(0.0) // we didn't parse the null
}
}
If one of these individually will not solve your error try both, it has to solve it.
I am building a chatting app using firebase realtime database.
I am storing the messages in both accounts simultaneously.
This is my fragment to send message:
class DmFragment : Fragment() {
lateinit var binding: FragmentDMBinding
lateinit var user: User
val list = ArrayList<Message>()
lateinit var auth: FirebaseAuth
lateinit var firebaseDatabase: FirebaseDatabase
lateinit var reference: DatabaseReference
lateinit var usersDMAdapter: UsersDMAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
user = it.getSerializable("user") as User
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentDMBinding.inflate(LayoutInflater.from(requireContext()), container, false)
auth = FirebaseAuth.getInstance()
val currentUser = auth.currentUser!!
firebaseDatabase = FirebaseDatabase.getInstance()
reference = firebaseDatabase.getReference("users")
binding.apply {
sendBtn.setOnClickListener {
val smsText = message.text.toString()
val simpleDateFormat = SimpleDateFormat("dd.MM.yyyy HH.ss")
val date = simpleDateFormat.format(Date())
val message1 = Message(smsText, date, currentUser.uid, user.uid)
val key = reference.push().key
reference.child("${currentUser.uid}/messages/${user.uid!!}/$key")
.setValue(message1)
reference.child("${user.uid}/messages/${currentUser.uid}/$key")
.setValue(message1)
message.text.clear()
}
reference.child("${currentUser.uid}/messages/${user.uid}")
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val children = snapshot.children
for (child in children) {
val value = child.getValue(Message::class.java)
if (value != null) {
list.add(value)
}
}
usersDMAdapter = UsersDMAdapter(list, currentUser.uid)
messageRv.adapter = usersDMAdapter
}
override fun onCancelled(error: DatabaseError) {
}
})
back.setOnClickListener {
findNavController().popBackStack()
}
userName.text = user.displayName
Picasso.get().load(user.photoUrl).into(userImage)
}
return binding.root
}
}
This is the adapter file that i used in the DMFragment:
class UsersDMAdapter(var list: List<Message>, var uid: String) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class FromVh(val fromBinding: ItemFromBinding) :
RecyclerView.ViewHolder(fromBinding.root) {
fun onBind(message: Message) {
fromBinding.apply {
messageTv.text = message.message
messageDateTv.text = message.date
}
}
}
inner class ToVh(val toBinding: ItemToBinding) :
RecyclerView.ViewHolder(toBinding.root) {
fun onBind(message: Message) {
toBinding.apply {
messageTv.text = message.message
messageDateTv.text = message.date
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == 1) {
return FromVh(
ItemFromBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
} else {
return ToVh(
ItemToBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (getItemViewType(position)==1){
val fromVh = holder as FromVh
fromVh.onBind(list[position])
}else{
val toVh = holder as ToVh
toVh.onBind(list[position])
}
}
override fun getItemViewType(position: Int): Int {
return if (list[position].fromUid == uid) {
1
} else{
2
}
}
override fun getItemCount(): Int = list.size
}
This is my fragment that shows all users excluding myself:
class UserRecyclerFragment : Fragment() {
lateinit var binding: FragmentUserRecyclerBinding
lateinit var userRvAdapter: UserRvAdapter
var list = ArrayList<User>()
lateinit var auth: FirebaseAuth
lateinit var firebaseDatabase: FirebaseDatabase
lateinit var reference: DatabaseReference
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentUserRecyclerBinding.inflate(
LayoutInflater.from(requireContext()),
container,
false
)
auth = FirebaseAuth.getInstance()
val currentUser = auth.currentUser!!
firebaseDatabase = FirebaseDatabase.getInstance()
reference = firebaseDatabase.getReference("users")
val email = currentUser.email
val displayName = currentUser.displayName
val photo = currentUser.photoUrl
val phoneNumber = currentUser.phoneNumber
val uid = currentUser.uid
val user = User(
email,
displayName,
photo.toString(),
phoneNumber,
uid
)
reference.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
list.clear()
val filterList = arrayListOf<User>()
val children = snapshot.children
for (child in children) {
val value = child.getValue(User::class.java)
if (value != null && uid != value.uid) {
list.add(value)
}
if (value != null && value.uid == uid) {
filterList.add(value)
}
}
if (filterList.isEmpty()) {
reference.child(uid).setValue(user)
}
userRvAdapter = UserRvAdapter(list, object : UserRvAdapter.OnItemClickListener {
override fun onItemCick(user: User) {
val bundle = Bundle()
bundle.putSerializable("user", user)
findNavController().navigate(R.id.dmFragment, bundle)
}
})
binding.rv.adapter = userRvAdapter
}
override fun onCancelled(error: DatabaseError) {
}
})
return binding.root
}
}
Whenever i chat (send/receive) in DM, it is showing messages. But when I go back, messages that added in my user account is being deleted automatically. If i come back again, it is not showing the messages that have been saved in other person's account either. And i do not know what to do. Can anyone help?
I have an application using databinding, livedata, room, kotlin koroutines, viewmodel, retrofit and koin. I have one activity, and two fragments.
UserListFragment: Show in a recyclerview a list of user items.
UserFullProfileFragment: Show the user item detail.
When the application is running, an external API is called to retrieve a list of users and display it in a recyclerview. Then, if I click on one item, an external API is called to get the detail of the current user whith its ID.
The problem is when I click on one item at the first, everything is going well but for following items, this is the detail of previous item which is displayed and so on and so forth.
Any ideas ?
UserRepository:
class UserRepositoryImpl (private val userApi: UserApi, private val userDao: UserDao, private val networkStateManager: NetworkStateManager) : UserRepository {
override suspend fun getUserList(): Result<List<UserListItem>> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user list from user API
val response = userApi.getUserList()
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$response")
val userList = userResponse.data
// convert user API object to user entity
val entities = userList.map { it.toUserEntity() }
// save user list in database
withContext(Dispatchers.IO) { userDao.addUsers(entities) }
// convert user entity to user model
val userItemList = entities.map { it.toUserListItem() }
return Result.Success(userItemList)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user list from database if no network
val data = withContext(Dispatchers.IO) { userDao.findAllUsers() }
return if (data.isNotEmpty()) {
Log.d("REPO", "get users from db")
val userItemList = data.map { it.toUserListItem() }
Result.Success(userItemList)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
override suspend fun getUserFullProfile(userId: String): Result<UserFullProfile> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user from user API
val response = userApi.getUserFullProfile(userId)
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$userResponse")
// convert user API object to user entity
val userEntity = userResponse.toUserEntity()
// save user data in database
withContext(Dispatchers.IO) { userDao.addUserFullProfile(userEntity) }
// convert user entity to user model
val user = userEntity.toUserFullProfile()
return Result.Success(user)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user from database if no network
val data = withContext(Dispatchers.IO) { userDao.getUserById(userId) }
return if (data != null) {
Log.d("REPO", "get users from db")
val user = data.toUserFullProfile()
Result.Success(user)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
UserViewModel:
getUserList and getUserFullProfile are use cases which call the repository
class UserViewModel (private val getUserList: GetUserList, private val getUserFullProfile: GetUserFullProfile) : ViewModel() {
val userList = MutableLiveData<List<UserListItem>>()
val userFullProfile = MutableLiveData<UserFullProfile>()
fun getUserList() {
viewModelScope.launch {
when (val result = getUserList.getUserList()) {
is Result.Success -> userList.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
fun getUserFullProfile(userId: String) {
viewModelScope.launch {
when (val result = getUserFullProfile.getUserFullProfile(userId)) {
is Result.Success -> userFullProfile.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
UserRecyclerAdaper:
class UserRecyclerAdapter(private val context: Context?, val clickListener: UserClickListener) : RecyclerView.Adapter<UserRecyclerAdapter.UserViewHolder>() {
var userList : List<UserListItem> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflatedView: UserItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.user_item, parent, false)
return UserViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bindUser(position)
}
override fun getItemCount() = userList.size
fun setUsers(users: List<UserListItem>) {
this.userList = users
notifyDataSetChanged()
}
inner class UserViewHolder(private val v: UserItemBinding) : RecyclerView.ViewHolder(v.root) {
fun bindUser(position: Int) {
val item = userList[position]
Log.d("ADAPTER", item.toString())
v.user = item
Picasso.get()
.load(item.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(v.picture)
v.userClickInterface = clickListener
v.root.setOnClickListener {
clickListener.onItemClick(item)
}
}
}
UserListFragment:
class UserListFragment : Fragment(), UserClickListener {
private val userViewModel by viewModel<UserViewModel>()
private lateinit var userAdapter: UserRecyclerAdapter
private lateinit var viewDataBinding: FragmentUserListBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_user_list, container, false)
viewDataBinding.lifecycleOwner = this
return viewDataBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
userAdapter = UserRecyclerAdapter(context, this)
recyclerView.adapter = userAdapter
recyclerView.isNestedScrollingEnabled = false
viewDataBinding.viewModel = userViewModel
userViewModel.getUserList()
userViewModel.userList.observe(viewLifecycleOwner, { userList ->
if (userList.isNotEmpty() && userList != null) {
userAdapter.setUsers(userList)
}
})
}
override fun onItemClick(user: UserListItem) {
Log.d("FRAGMENT", user.toString())
userViewModel.getUserFullProfile(user.id)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
Log.d("UFP", userFullProfile.toString())
if (userFullProfile != null) {
(activity as MainActivity).replaceFragment(UserFullProfileFragment.newInstance(userFullProfile),
R.id.fragment_layout, "userFullProfile")
}
})
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
companion object {
#JvmStatic
fun newInstance(user: UserFullProfile) = UserFullProfileFragment().apply {
arguments = Bundle().apply {
putParcelable("user", user)
}
}
}
private var user: UserFullProfile? = null
private lateinit var mViewDataBinding: FragmentUserFullProfileBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewDataBinding.user = user
notify()
Picasso.get()
.load(mViewDataBinding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(mViewDataBinding.picture)
val dateOfBirth = parseDate(mViewDataBinding.user?.dateOfBirth)
mViewDataBinding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(mViewDataBinding.user?.registerDate)
mViewDataBinding.registerDate.text = registerDate
}
override fun onAttach(context: Context) {
super.onAttach(context)
user = arguments?.getParcelable("user")
Log.d("APP", user.toString())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
mViewDataBinding.lifecycleOwner = this
return mViewDataBinding.root
}
Thank you :)
Finally, I found a solution :
I pass the user id argment from UserListFragment to UserFullProfileFragment instead of the current user object and I call the external API to get the current user in the UserFullProfileFragment.
This is the final code:
UserListFragment:
override fun onItemClick(user: UserListItem) {
val action = UserListFragmentDirections.actionUserListFragmentToUserFullProfileFragment(user.id)
findNavController().navigate(action)
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
private lateinit var userID: String
private var mViewDataBinding: FragmentUserFullProfileBinding? = null
private val binding get() = mViewDataBinding!!
private val userViewModel by viewModel<UserViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = userViewModel
userID = UserFullProfileFragmentArgs.fromBundle(requireArguments()).userArgs
userViewModel.getUserFullProfile(userID)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
if (userFullProfile != null) {
binding.user = userFullProfile
Picasso.get()
.load(binding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(binding.picture)
val dateOfBirth = parseDate(binding.user?.dateOfBirth)
binding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(binding.user?.registerDate)
binding.registerDate.text = registerDate
}
})
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
binding.lifecycleOwner = this
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
mViewDataBinding = null
}
}
1.The "address" variable here gives me a city name with coordinates.I would like to write this address instead of Istanbul in the retrofit so that it can automatically show the weather of the selected location on the map.(Googlemap)
private const val TAG = "MapViewFragment"
class MapViewFragment: Fragment(), OnMapReadyCallback {
companion object{
private lateinit var nMap: GoogleMap
var address:String = ""
var test:String = ""
var test2:String = ""
var cacik:String = "adana"
private var markers:MutableList<Marker> = mutableListOf<Marker>()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
map_view.onCreate(savedInstanceState)
map_view.onResume()
map_view.getMapAsync(this)
setToolbar()
}
override fun onMapReady(map: GoogleMap?) {
if (map != null) {
nMap = map
}
map?.let {
nMap = it
nMap.setOnInfoWindowClickListener { markerToDelete ->
Log.i(TAG, "onWindowsClickListener - Delete Thismarker")
markers.remove(markerToDelete)
markerToDelete.remove()
}
nMap.setOnMapLongClickListener { latlng ->
Log.i(TAG, "onMapLongClickListener" + latlng)
Toast.makeText(
requireContext(),
"this is toast message" + latlng,
Toast.LENGTH_SHORT
).show()
showAlertDialog(latlng)
address= getAddress(latlng.latitude, latlng.longitude)
test = getAddress(37.000000,35.321335)
test2 = "istanbul"
Log.d(TAG,"test5 $address ${latlng.latitude} ${latlng.longitude}")
Toast.makeText(requireContext(),"test"+address,Toast.LENGTH_LONG).show()
}
}
}
private fun getAddress(lat: Double, lng: Double): String {
val geocoder = Geocoder(requireContext())
val list = geocoder.getFromLocation(lat, lng, 1)
return list[0].getAddressLine(0)
//val stateName: String = addresses[0].getAddressLine(1)
}
private fun showAlertDialog(latlng: LatLng) {
val dialog =
AlertDialog.Builder(requireContext())
.setTitle("Create a marker").setMessage("add marker...")
.setNegativeButton("Cancel", null)
.setPositiveButton("Ok", null)
.show()
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
val marker = nMap.addMarker(
MarkerOptions().position(latlng).title("my new marker").snippet(
"a cool snippet"
)
)
markers.add(marker)
dialog.dismiss()
}
dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener {
dialog.dismiss()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.mapviewfragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun setToolbar(){
val actionBar : ActionBar? =(requireActivity() as MainActivity).supportActionBar
actionBar?.apply {
title = getString(R.string.Kartenansicht)
setDisplayShowTitleEnabled(true)
setHomeButtonEnabled(true)
}
}
}
3.i just want to call "address" in my retrofit response .I want to call the address on the other class and write the location of Istanbul.The getter and setter methods don't work because I haven't written them inside the class.How can I use the address in another class.
private const val TAG = "Retrofit Connection"
fun main(){
retrofitResponse()
}
object RetrofitSetup {
//var test = MapViewFragment.address
var urlAll = "api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}"
var url = "https://api.openweathermap.org/data/2.5/"
val apiKey = "d459f98ffa705ad3f6c5e02f86d9fab9"
}
fun retrofitResponse(){
val retrofit = Retrofit.Builder()
.baseUrl(RetrofitSetup.url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val weatherApi = retrofit.create(CallWeatherApi::class.java)
val weatherResponseCall = weatherApi.getWeather(MapViewFragment.test,RetrofitSetup.apiKey)
weatherResponseCall!!.enqueue(object : Callback<CurrentWeatherResponse?> {
override fun onResponse(call: Call<CurrentWeatherResponse?>, response: Response<CurrentWeatherResponse?>
) {
if (response.code() == 404) {
Log.d(TAG,"Successfuly")
} else if (!response.isSuccessful) {
Log.d(TAG,"Error")
}
val mydata = response.body()
val main = mydata!!.main
val temp = main!!.temp
val pres =main!!.pressure
val temperature = (temp!! - 273.15).toInt()
Log.d("TAG","City pressure :" + pres)
Log.d("TAG","City Temp : " + temperature)
}
override fun onFailure(call: Call<CurrentWeatherResponse?>, t: Throwable) {}
})
}
if i write default istanbul.its working
if i create another varible in other class dosent work
exception
put "retrofitResponse" inside "RetrofitSetup" and add a parameter for the addresss
object RetrofitSetup {
private const val urlAll = "api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}"
private const val url = "https://api.openweathermap.org/data/2.5/"
private const val apiKey = "keep this private"
fun retrofitResponse(address: String) {
...
val weatherResponseCall = weatherApi.getWeather(address, apiKey)
...
}
}
then pass the address as argument
nMap.setOnMapLongClickListener { latlng ->
val address = getAddress(latlng.latitude, latlng.longitude)
RetrofitSetup.retrofitResponse(address)
}
Bear with me - new to Kotlin so probably missing something obvious. As commented in the fragment code 'myPolyLine?.points = it' doesn't work but 'myPolyLine?.points = pLines.value' does (problem being that if I navigate away from the fragment I lose the existing polyline). Snip from Fragment:
class MapFragment : Fragment() {
private lateinit var mapViewModel: MapViewModel
private var myPositionMarker: Marker? = null
private lateinit var googleMap: GoogleMap
private var myPolyLine: Polyline? = null
private var isPolyAdded: Boolean = false
private lateinit var mo:MarkerOptions
private var pLines:MutableLiveData<ArrayList<LatLng>> = MutableLiveData<ArrayList<LatLng>>()
private val callback = OnMapReadyCallback { googleMap ->
this.googleMap = googleMap
googleMap.animateCamera(CameraUpdateFactory.zoomTo(12.0F))
mo = MarkerOptions()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
pLines.value = ArrayList<LatLng>()
mapViewModel = ViewModelProvider(this).get(MapViewModel::class.java)
mapViewModel.theLocation.observe(viewLifecycleOwner, Observer {
val position = LatLng(it.latitude, it.longitude)
googleMap.moveCamera(CameraUpdateFactory.newLatLng(position))
googleMap.animateCamera(CameraUpdateFactory.zoomTo(12.0F))
if (myPositionMarker == null) {
myPositionMarker = googleMap.addMarker(
mo.position(position).title("Me")
)
} else {
myPositionMarker!!.position = position
}
pLines.value?.add(position)
})
mapViewModel.points.observe(viewLifecycleOwner, Observer {
if (this::googleMap.isInitialized) {
if (!isPolyAdded) {
var lineOptions = PolylineOptions()
.color(Color.RED)
.width(8F)
myPolyLine = googleMap.addPolyline(lineOptions)
isPolyAdded = true
} else {
//Why doesn't this work (doesn't show polyline):
myPolyLine?.points = it
//But this does:
myPolyLine?.points = pLines.value
//Shows expected size in both cases
Log.d("Fragment", "myPolyLine has ${myPolyLine?.points?.size} points")
}
}
})
return inflater.inflate(R.layout.fragment_map, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(callback)
}
}
ViewModel:
class MapViewModel(application: Application) : AndroidViewModel(application){
private val repo = LocationRepository(application)
lateinit var theLocation: LiveData<Location>
lateinit var points: LiveData<ArrayList<LatLng>>
init {
theLocation = repo.theLocation
points = repo.points
}
override fun onCleared() {
Log.d("View Model", " Cleared")
super.onCleared()
repo.unregister()
}
}
Repository:
class LocationRepository(context: Context) {
var theLocation: MutableLiveData<Location> = MutableLiveData()
private var internalPoints: ArrayList<LatLng> = ArrayList()
var points: MutableLiveData<ArrayList<LatLng>> = MutableLiveData()
val context: Context = context
private val lr0: BroadcastReceiver
init {
points.value = ArrayList<LatLng>()
// points = MutableLiveData<ArrayList<LatLng>>()
lr0 = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val currentlocation = intent?.getParcelableExtra<Location>("Location")
theLocation.setValue(currentlocation)
if (currentlocation != null) {
internalPoints.add(LatLng(currentlocation.longitude, currentlocation.latitude))
points.value = internalPoints
}
}
}
Log.d("Repo"," Registering receiver")
val intentFilter = IntentFilter("locationChange")
context.registerReceiver(lr0, intentFilter)
}
fun unregister() {
Log.d("Repo"," Unregistering receiver")
context.unregisterReceiver(lr0)
}
}