Firebase Realtime Database Tracking MVP Part 2

Hi Coder !

Seperti janji admin kemarin, Kita akan melanjutkan untuk feature dari tracking yang belum kita finish seperti :

  • List User
  • Tracking Maps

Pada artikel ini kita akan menyelesaikan semua feature nya sehingga aplikasi nya berjalan untuk tracking berdasarkan latitude dan longitude current location kita. Dan kita akan menyelesaikan List User terlebih dahulu.

Selanjutnya pada class ListUserContract buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui.user

import com.google.firebase.database.DatabaseReference
import com.syntax.tutorialtrackingmvp.base.BaseView
import com.syntax.tutorialtrackingmvp.model.Users

interface ListUserContract {

interface PresenterInterface {
fun doGetUser(database: DatabaseReference?)
}

interface ViewInterface : BaseView {
fun isSuccess(data: List<Users>)
fun isError(msg : String)
}
}

Selanjutnya pada class ListUserPresenter buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui.user

import android.util.Log
import com.google.firebase.database.*
import com.syntax.tutorialtrackingmvp.base.BasePresenter
import com.syntax.tutorialtrackingmvp.model.Users

class ListUserPresenter(var _view: ListUserContract.ViewInterface? = null) :
BasePresenter<ListUserContract.ViewInterface>, ListUserContract.PresenterInterface {

var data: MutableList<Users> = mutableListOf()

override fun onAttach(view: ListUserContract.ViewInterface) {
_view = view
}

override fun onDettah() {
_view = null
}

override fun doGetUser(database: DatabaseReference?) {

// Read from the database
database?.addValueEventListener(object : ValueEventListener {

// This method is called once with the initial value and again
// whenever data at this location is updated
override fun onDataChange(p0: DataSnapshot) {
data.clear()
for (dataSnapshot in p0.children) {
val users = Users()
val getDataUser = dataSnapshot.getValue(Users::class.java)

// Set data to model user
users.uuid = getDataUser?.uuid!!
users.name = getDataUser.name
users.email = getDataUser.email

data.addAll(listOf(users))
}
Log.d("TAG", "Read values is success")
_view?.isSuccess(data)
}

override fun onCancelled(p0: DatabaseError) {
// Failed to read value
Log.w("TAG", "Failed to read value : ${p0.toException()}")
_view?.isError(p0.toException().toString())
}
})
}
}

Selanjutnya pada class ListUserAdapter buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui.user

import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.syntax.tutorialtrackingmvp.R
import com.syntax.tutorialtrackingmvp.model.Users
import com.syntax.tutorialtrackingmvp.ui.MapsActivity
import kotlinx.android.synthetic.main.item_list_user.view.*

class ListUserAdapter(private var data: List<Users>) :
RecyclerView.Adapter<ListUserAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListUserAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_list_user, parent, false)
return ViewHolder(view)
}

@Suppress("USELESS_ELVIS")
override fun getItemCount(): Int = data.size ?: 0

override fun onBindViewHolder(holder: ListUserAdapter.ViewHolder, position: Int) {
holder.bindItem(data.get(position))
holder.itemView.setOnClickListener {

val intent = Intent(holder.itemView.context, MapsActivity::class.java)
intent.putExtra("id", data.get(position).uuid)
holder.itemView.context.startActivity(intent)
}
}

class ViewHolder(view: View?) : RecyclerView.ViewHolder(view!!) {
fun bindItem(get: Users) {
itemView.tv_name.text = get.name
itemView.tv_email.text = get.email
}
}
}

Selanjutnya pada class ListUserAcitivity buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui.user

import android.content.Intent
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.syntax.tutorialtrackingmvp.R
import com.syntax.tutorialtrackingmvp.model.Users
import com.syntax.tutorialtrackingmvp.service.TrackingService
import kotlinx.android.synthetic.main.activity_list_user.*
import org.jetbrains.anko.toast

class ListUserActivity : AppCompatActivity(), ListUserContract.ViewInterface {

private val REQUEST_PERMISSIONS = 1
private var database: DatabaseReference? = null

private val presenter by lazy {
ListUserPresenter()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_user)

presenter
onAttachView()

// Write database user
database = FirebaseDatabase.getInstance().reference.child("user")

//Check whether this app has access to the location permission//
val permission = ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
)

// If the location permission has been granted, then start the TrackerService
if (permission == PackageManager.PERMISSION_GRANTED) {
startTrackerService()
} else {

// If the app doesn’t currently have access to the user’s location, then request access
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS
)
}
presenter.doGetUser(database)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {

// If the permission has been granted
if (requestCode == REQUEST_PERMISSIONS && grantResults.size == 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
// then start the GPS tracking service
startTrackerService()
} else {
// If the user denies the permission request, then display a toast with some more information
toast("Please enable location services to allow GPS tracking")
}
}

private fun startTrackerService() {
startService(Intent(this, TrackingService::class.java))
}

override fun onAttachView() {
presenter.onAttach(this)
}

override fun onDettachView() {
presenter.onDettah()
}

override fun isSuccess(data: List<Users>) {
recyclerview.setHasFixedSize(true)
recyclerview.adapter = ListUserAdapter(data)
recyclerview.layoutManager = LinearLayoutManager(this)
}

override fun isError(msg: String) {
toast(msg)
}

override fun onStart() {
super.onStart()
onAttachView()
}

override fun onDestroy() {
super.onDestroy()
onDettachView()
}
}

Selanjutnya silahkan di running pada device Android atau Emulator kita dan List User akan tampil di ListUserActivity dalam recyclerview.

Selanjutnya adalah ketika list user melakukan suatu event klik listener pada masing-masing item akan passing data ke MapsActivity berdasarkan uuid perhatikan approach code berikut ini di dalam adapter kita :

val intent = Intent(holder.itemView.context, MapsActivity::class.java)
intent.putExtra("id", data.get(position).uuid)
holder.itemView.context.startActivity(intent)

Dan pada MapsActivity kita akan write database Locations nya dan akan secara realtime store data latitude dan longitude ke Firebase Realtime Database. Sebelum mengimplementasi approach code di MapsActivity seperti apa, silahkan terlebih dauhulu unttuk memahami apa itu Service : https://developer.android.com/guide/components/services

Selanjutnya pada class TrackingService buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.service

import android.Manifest
import android.annotation.SuppressLint
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.IBinder
import androidx.core.content.ContextCompat
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase


class TrackingService : Service() {

private var mAuth: FirebaseAuth? = null

override fun onBind(intent: Intent): IBinder? {
return null
}

override fun onCreate() {
super.onCreate()
mAuth = FirebaseAuth.getInstance()
requestLocationUpdates()
}

var stopReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {

// Unregister the BroadcastReceiver when the notification is tapped
unregisterReceiver(this)

//Stop the Service//
stopSelf()
}
}

private fun requestLocationUpdates() {
val request = LocationRequest()

// Specify how often your app should request the device’s location
request.setInterval(1000) // 1 seconds

// Get the most accurate location data available
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
val client = LocationServices.getFusedLocationProviderClient(this)
val path = "locations"
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)

// If the app currently has access to the location permission
if (permission == PackageManager.PERMISSION_GRANTED) {

// then request location updates//
client.requestLocationUpdates(request, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {

// Get a reference to the database, so your app can perform read and write operations
val dr = FirebaseDatabase.getInstance().getReference(path)
val location = locationResult.getLastLocation()
if (location != null) {
// Save the location data to the database//
dr.child(mAuth?.currentUser?.uid!!).setValue(location)
}
}
}, null)
}
}
}

Jangan lupa untuk tambahkan Permission manifest dan kenalin Service nya seperti ini :

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<dist:module dist:instant="true"/>

<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" tools:ignore="GoogleAppIndexingWarning">

<service android:name=".service.TrackingService" />

</application>

Selanjutnya pada class MapsContract buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui

import com.google.android.gms.maps.model.LatLng
import com.google.firebase.database.DatabaseException
import com.google.firebase.database.DatabaseReference
import com.syntax.tutorialtrackingmvp.base.BaseView

interface MapsContract {

interface PresenterInterface {
fun doGetLocation(dr: DatabaseReference?)
}

interface ViewInterface : BaseView {
fun isSuccess(latLng: LatLng?)
fun isError(msg: String)
}
}

Selanjutnya pada class MapsPresenter buat approach code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui

import android.util.Log
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import com.syntax.tutorialtrackingmvp.base.BasePresenter
import com.syntax.tutorialtrackingmvp.model.Locations

class MapsPresenter(var _view: MapsContract.ViewInterface? = null) : BasePresenter<MapsContract.ViewInterface>,
MapsContract.PresenterInterface {

override fun onAttach(view: MapsContract.ViewInterface) {
_view = view
}

override fun onDettah() {
_view = null
}

override fun doGetLocation(dr: DatabaseReference?) {
dr?.addValueEventListener(object : ValueEventListener {

override fun onDataChange(p0: DataSnapshot) {

// get location user
val getDataLocation = p0.getValue(Locations::class.java)

// lat & long
val lat = getDataLocation?.latitude
val lon = getDataLocation?.longitude
val latLng = LatLng(lat!!, lon!!)
Log.d("TAG", "Read values is success : ${latLng}")

// passing data lat & long
_view?.isSuccess(latLng)
}

override fun onCancelled(p0: DatabaseError) {
// Failed to read value
Log.w("TAG", "Failed to read value : ${p0.toException()}")
_view?.isError("Failed to read value : ${p0.toException()}")
}
})
}
}

Selanjutnya approach yang terakhir buat code nya seperti ini :

package com.syntax.tutorialtrackingmvp.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.firebase.database.*
import com.syntax.tutorialtrackingmvp.R
import com.syntax.tutorialtrackingmvp.model.Locations
import org.jetbrains.anko.toast

class MapsActivity : AppCompatActivity(), OnMapReadyCallback, MapsContract.ViewInterface {


private lateinit var mMap: GoogleMap
private var dr: DatabaseReference? = null

private val presenter by lazy {
MapsPresenter()
}

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)
presenter
onAttachView()

// Write database locations
dr = FirebaseDatabase.getInstance().getReference().child("locations").child(intent.getStringExtra("id"))
presenter.doGetLocation(dr)

}

override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
}

override fun isSuccess(latLng: LatLng?) {
mMap.clear()
mMap.addMarker(MarkerOptions().position(latLng!!).title("Marker in My Location"))
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16f))
}

override fun isError(msg: String) {
toast(msg)
}

override fun onAttachView() {
presenter.onAttach(this)
}

override fun onDettachView() {
presenter.onDettah()
}

override fun onStart() {
super.onStart()
onAttachView()
}

override fun onDestroy() {
super.onDestroy()
onDettachView()
}
}

Selanjutnya perhatikan approach code berikut ini :

// Write database locations
dr = FirebaseDatabase.getInstance().getReference().child("locations").child(intent.getStringExtra("id"))

Approach code diatas untuk membuat database dengan nama locations dan akan secara otomatis dan Realtime Database terbuat seperti contoh gambar berikut ini :

Jika ada pertanyaan atau ada yang kurang jelas silahkan tinggalkan pertanyaan pada kolom komentar dibawah ya !

Happy Coding !

1
Hello, welcome to IMA Studio 🙌
Chat with us ! We are ready to help you :-)
Powered by