Skip to content

Instantly share code, notes, and snippets.

@ayetolusamuel
Created September 5, 2021 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ayetolusamuel/99281e26a052830b471b89b891204c45 to your computer and use it in GitHub Desktop.
Save ayetolusamuel/99281e26a052830b471b89b891204c45 to your computer and use it in GitHub Desktop.
package com.chamsmobile.android.features.accountcreation.agent.presentation.ui
import android.Manifest
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.chamsmobile.android.R
import com.chamsmobile.android.core.utils.KegowSharedPreferences
import com.chamsmobile.android.core.utils.ProgressLoader
import com.chamsmobile.android.core.utils.getAgentData
import com.chamsmobile.android.core.utils.hide
import com.chamsmobile.android.core.utils.isValidEmail
import com.chamsmobile.android.core.utils.remove
import com.chamsmobile.android.core.utils.show
import com.chamsmobile.android.core.utils.tempSaveAgentData
import com.chamsmobile.android.core.utils.viewBinding
import com.chamsmobile.android.databinding.FragmentAgentAccountBinding
import com.chamsmobile.android.features.accountcreation.agent.presentation.viewmodel.AgentViewModel
import com.chamsmobile.android.features.accountcreation.agent.presentation.viewstate.CountriesViewState
import com.chamsmobile.android.features.accountcreation.agent.presentation.viewstate.StateViewState
import com.chamsmobile.android.features.accountcreation.agent.remote.model.body.AgentRemoteBody
import com.chamsmobile.android.features.accountcreation.agent.remote.model.response.Country
import com.chamsmobile.android.features.accountcreation.agent.remote.model.response.State
import com.chamsmobile.android.features.setting.SettingFragmentArgs
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.FusedLocationProviderClient
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.android.gms.location.LocationSettingsRequest
import com.google.android.gms.location.LocationSettingsResponse
import com.google.android.gms.location.LocationSettingsStatusCodes
import com.google.android.gms.tasks.Task
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import javax.inject.Inject
@AndroidEntryPoint
class AgentAccountFragment : Fragment(R.layout.fragment_agent_account) {
private val binding by viewBinding(FragmentAgentAccountBinding::bind)
@Inject
lateinit var progressLoader: ProgressLoader
private val viewModel: AgentViewModel by viewModels()
private val args: SettingFragmentArgs by navArgs()
var defCountry: Country? = null
var defState: State? = null
private var mLocation: Location? = null
private lateinit var locationManager: LocationManager
private lateinit var locationCallback: LocationCallback
/**
* Provides the entry point to the Fused Location Provider API.
*/
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val agentDataTracker = KegowSharedPreferences(requireContext()).getInt("agent_data_tracker")
if (agentDataTracker == 0) {
tempSaveAgentData(null, requireContext())
}
activity?.onBackPressedDispatcher?.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
findNavController().navigateUp()
}
}
)
/* register broadcast receivers */
val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
filter.addAction(Intent.ACTION_PROVIDER_CHANGED)
requireContext().registerReceiver(broadcastReceiver, filter)
locationManager = requireContext()
.getSystemService(Context.LOCATION_SERVICE) as LocationManager
/* Handles the result of location request */
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations) {
if (locationResult.locations.size > 0)
mLocation = location
}
}
}
fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_agent_account, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val agentDataTracker = KegowSharedPreferences(requireContext()).getInt("agent_data_tracker")
if (agentDataTracker == 0) {
tempSaveAgentData(null, requireContext())
binding.email.setText("")
binding.addressStreet.setText("")
binding.businessType.setText("")
binding.addressLocalGovt.setText("")
}
setupView()
with(binding) {
backButton.setOnClickListener {
try {
findNavController().navigateUp()
} catch (exc: Exception) {
Log.e(TAG, exc.toString())
}
}
}
binding.next.setOnClickListener {
if (checkPermissions()) {
if (requestingLocationUpdates()) {
saveAgentDetails()
} else {
getCurrentLocation()
}
} else {
requestPermissions()
}
}
binding.backButton.setOnClickListener {
findNavController().navigateUp()
}
}
private fun showStateBottomSheet(states: List<State>) {
activity?.supportFragmentManager?.let {
StatesSelectionBottomSheetFragment(states).apply {
doOnStateSelected { state ->
binding.addressState.setText(state.name)
binding.addressLocalGovt.show()
defState = state
}
show(it, tag)
}
}
}
private fun setupView() {
binding.businessName.doAfterTextChanged {
if (it.length > 2) {
binding.businessType.show()
} else {
binding.businessType.remove()
}
}
binding.businessType.doAfterTextChanged {
if (it.length > 1) {
binding.email.show()
} else {
binding.email.remove()
}
}
binding.email.doAfterTextChanged {
if (it.length > 5 && isValidEmail()) {
binding.addressCountry.show()
} else {
binding.addressCountry.remove()
}
}
binding.addressCountry.setOnClickListener {
viewModel.getCountries()
binding.addressState.remove()
}
binding.addressState.setOnClickListener {
if (defCountry != null) {
viewModel.getStates(defCountry?.id.toString())
binding.addressLocalGovt.remove()
}
}
binding.addressLocalGovt.doAfterTextChanged {
if (it.length >= 3) {
binding.addressHouseNumber.show()
} else {
binding.addressHouseNumber.hide()
}
}
binding.addressHouseNumber.doAfterTextChanged {
if (it.isNotEmpty()) {
binding.addressStreet.show()
} else {
binding.addressStreet.hide()
}
}
binding.addressStreet.doAfterTextChanged {
if (it.length >= 3) {
binding.busStopLandmark.show()
} else {
binding.busStopLandmark.hide()
}
}
binding.busStopLandmark.doAfterTextChanged {
binding.next.isEnabled = it.length >= 3
}
}
private fun showCountryBottomSheet(countries: List<Country>) {
activity?.supportFragmentManager?.let {
CountriesSelectionBottomSheetFragment(countries).apply {
doOnCountrySelected { country ->
binding.addressCountry.setText(country.name)
defCountry = country
binding.addressState.show()
}
show(it, tag)
}
}
}
private fun saveAgentDetails() {
if (mLocation != null) {
val landmark = binding.busStopLandmark.text
val email = binding.email.text
val houseNumber = binding.addressHouseNumber.text
val localGovt = binding.addressLocalGovt.text
val stateId = defState?.id
val stateName = defState?.name
val street = binding.addressStreet.text
val lat = mLocation?.latitude
val long = mLocation?.longitude
val businessName = binding.businessName.text
val businessType = binding.businessType.text
Log.d("Latitude and Longitude", "Location: $lat + $long")
val agentBodyModel = AgentRemoteBody(
address_bus_stop_landmark = landmark,
address_house_number = houseNumber,
address_local_govt = localGovt,
address_state_id = stateId!!,
address_state_name = stateName!!,
address_street = street,
address_gps_latitude = lat.toString(),
address_gps_longitude = long.toString(),
business_name = businessName,
business_type = businessType,
email = email
)
val action =
AgentAccountFragmentDirections.actionAgentAccountCreationFragmentToAgentSummaryFragment(
agent = agentBodyModel,
country = defCountry!!.name, args.user
)
findNavController().navigate(action)
tempSaveAgentData(agentRemoteBody = agentBodyModel, context = requireContext())
KegowSharedPreferences(requireContext()).setInt("agent_data_tracker", 1)
} else {
Toast.makeText(requireContext(), "Unable to detect location..", Toast.LENGTH_SHORT).show()
}
}
private fun isValidEmail(): Boolean {
if (!binding.email.text.isValidEmail()) {
try {
val data = getAgentData(requireContext())
if (data == null) {
Toast.makeText(requireContext(), "Invalid email address", Toast.LENGTH_SHORT)
.show()
return false
}
} catch (exc: Exception) {
Log.e(TAG, "Exception: " + exc.printStackTrace())
}
}
return true
}
private fun agentObserver() {
lifecycleScope.launchWhenCreated {
viewModel.getCountriesFlow.collect {
when (it) {
is CountriesViewState.Loading -> {
val message = it.message?.let { a ->
getString(a)
}
progressLoader.show(message)
}
is CountriesViewState.Success -> {
progressLoader.hide()
it.countriesRemoteResponse.let { countries ->
if (countries != null) {
showCountryBottomSheet(countries.countries)
}
}
}
is CountriesViewState.Failure -> {
progressLoader.hide()
Toast.makeText(context, getString(it.message), Toast.LENGTH_LONG).show()
}
else -> Unit
}
}
}
lifecycleScope.launchWhenCreated {
viewModel.getStatesFlow.collect {
when (it) {
is StateViewState.Loading -> {
val message = it.message?.let { a ->
getString(a)
}
progressLoader.show(message)
}
is StateViewState.Success -> {
progressLoader.hide()
it.stateRemoteResponse.let { state ->
if (state != null) {
showStateBottomSheet(state.states)
}
}
}
is StateViewState.Failure -> {
progressLoader.hide()
Toast.makeText(context, getString(it.message), Toast.LENGTH_LONG).show()
}
else -> Unit
}
}
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
var data = getAgentData(requireContext())
if (data != null) {
setData(data)
data = null
tempSaveAgentData(data, requireContext())
} else {
agentObserver()
}
}
private fun setData(agentRemoteBody: AgentRemoteBody?) {
if (agentRemoteBody != null) {
binding.addressLocalGovt.show()
binding.addressCountry.show()
binding.addressState.show()
binding.businessName.setText(agentRemoteBody.business_name)
binding.businessType.setText(agentRemoteBody.business_type)
binding.email.setText(agentRemoteBody.email)
binding.busStopLandmark.setText(agentRemoteBody.address_bus_stop_landmark)
binding.addressStreet.setText(agentRemoteBody.address_street)
binding.addressHouseNumber.setText(agentRemoteBody.address_house_number)
binding.addressLocalGovt.setText(agentRemoteBody.address_local_govt)
binding.addressState.setText(agentRemoteBody.address_state_name)
if (defCountry != null) {
binding.addressCountry.setText(defCountry!!.name)
}
}
}
override fun onResume() {
super.onResume()
if (requestingLocationUpdates()) startLocationUpdates()
}
@SuppressLint("MissingPermission")
private fun startLocationUpdates() {
val locationRequest: LocationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = 30 * 1000
locationRequest.fastestInterval = 5 * 1000
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
private fun checkPlayServices(): Boolean {
val apiAvailability = GoogleApiAvailability.getInstance()
val resultCode = apiAvailability.isGooglePlayServicesAvailable(requireContext())
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(
this,
resultCode,
PLAY_SERVICES_RESOLUTION_REQUEST
)
} else {
requireActivity().finish()
}
return false
}
return true
}
/**
* Get the user's current location
*/
private fun getCurrentLocation() {
val locationRequest: LocationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = 30 * 1000
locationRequest.fastestInterval = 5 * 1000
// Settings client is required to check which settings are enabled, and present the
// Location Settings dialog for the user to update their settings with a single tap
val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
builder.setAlwaysShow(true) // this is the key ingredient
val result: Task<LocationSettingsResponse> = LocationServices.getSettingsClient(
requireContext()
).checkLocationSettings(builder.build())
result.addOnCompleteListener { task ->
try {
val response: LocationSettingsResponse = task.getResult(ApiException::class.java)
/**
* All location settings are satisfied. The client can initialize location requests here.
*/
} catch (exception: ApiException) {
when (exception.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
// Location settings are not satisfied. But could
// be fixed by showing the user a dialog.
try {
// Cast to a resolvable exception.
val resolvable = exception as ResolvableApiException
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
resolvable.startResolutionForResult(
requireActivity(),
REQUEST_CHECK_SETTINGS
)
} catch (e: IntentSender.SendIntentException) {
// Ignore the error.
} catch (e: ClassCastException) {
// Ignore, should be an impossible error.
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
}
}
}
}
}
// /* set broadcast receiver to detect GPS changes */
private var broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
/* listen for changes in cell broadcast */
if (LocationManager.PROVIDERS_CHANGED_ACTION == intent.action) {
val locationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
// val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
if (isGpsEnabled) {
// Handle Location turned ON
Toast.makeText(requireContext(), "LOCATION ENABLED", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(requireContext(), "LOCATION DISABLED", Toast.LENGTH_LONG).show()
// Handle Location turned OFF
}
}
}
}
/**
* Method to check if location is enabled
* @return true || false
*/
private fun requestingLocationUpdates(): Boolean {
val locationManager = requireActivity()
.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
return locationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER) == true ||
locationManager?.isProviderEnabled(LocationManager.NETWORK_PROVIDER) == true
}
@SuppressLint("MissingPermission")
private fun getLastLocation() {
fusedLocationClient.lastLocation
.addOnCompleteListener { taskLocation ->
if (taskLocation.isSuccessful && taskLocation.result != null) {
val loc = taskLocation.result
mLocation = loc
} else {
Log.w(TAG, "getLastLocation:exception", taskLocation.exception)
showSnackbar(R.string.no_location_detected)
}
}
}
private fun showSnackbar(
snackStrId: Int,
actionStrId: Int = 0,
listener: View.OnClickListener? = null,
) {
val snackbar = Snackbar.make(
requireActivity().findViewById(android.R.id.content),
getString(snackStrId),
Snackbar.LENGTH_LONG
)
if (actionStrId != 0 && listener != null) {
snackbar.setAction(getString(actionStrId), listener)
}
snackbar.show()
}
private fun requestPermissions() {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
),
LOCATION_PERMISSION_REQUEST_CODE
)
}
private fun checkPermissions(): Boolean {
return ActivityCompat.checkSelfPermission(requireActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
// If we want background location
// on Android 10.0 and higher,
// use:
// ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
}
companion object {
private const val TAG = "Agent Account Creation"
private const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000
private const val REQUEST_CHECK_SETTINGS = 100
private const val LOCATION_PERMISSION_REQUEST_CODE = 5
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment