diff --git a/.idea/misc.xml b/.idea/misc.xml index e41c484c..918a92d8 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -10,7 +10,7 @@ diff --git a/app/build.gradle b/app/build.gradle index c1be5151..ddd93edd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -185,5 +185,12 @@ dependencies { // Daraja implementation 'com.androidstudy.daraja:daraja:2.0.2' + // In App Updates + implementation("com.google.android.play:app-update-ktx:2.1.0") + + // Google Firebase Analytics + implementation(platform("com.google.firebase:firebase-bom:33.4.0")) + implementation("com.google.firebase:firebase-analytics") + } \ No newline at end of file diff --git a/app/release/app-release.aab b/app/release/app-release.aab new file mode 100644 index 00000000..6df1a359 Binary files /dev/null and b/app/release/app-release.aab differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1ac4e996..aff36a5e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,12 @@ + + + + @@ -11,6 +17,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/ShambaApp.kt b/app/src/main/java/com/steve_md/smartmkulima/ShambaApp.kt index aeb3760d..9fdf6efa 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ShambaApp.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ShambaApp.kt @@ -1,16 +1,17 @@ package com.steve_md.smartmkulima -import android.app.Activity import android.app.Application import android.content.Context import android.content.SharedPreferences -import android.os.Build +import android.os.Bundle import android.os.StrictMode import androidx.appcompat.app.AppCompatDelegate +import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.database.FirebaseDatabase -import com.steve_md.smartmkulima.utils.SnackbarHelper import dagger.hilt.android.HiltAndroidApp import timber.log.Timber +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors /** @@ -19,16 +20,55 @@ import timber.log.Timber */ @HiltAndroidApp class ShambaApp : Application() { + + private lateinit var firebaseAnalytics: FirebaseAnalytics override fun onCreate() { super.onCreate() timber() - setNetworkSecurity() + // setNetworkSecurity() FirebaseDatabase.getInstance().setPersistenceEnabled(true) val sharedPreferences: SharedPreferences = getSharedPreferences("ui_mode", Context.MODE_PRIVATE) val itemUIMode: Boolean = sharedPreferences.getBoolean("ISCHECKED", false) Timber.d("UI Theme: $itemUIMode") uiMode(itemUIMode) + + initializeSDKS() + } + + private fun initializeSDKS() { + val executorService: ExecutorService = Executors.newFixedThreadPool(2) + executorService.execute { + initializeAnalyticsSDKS(context = this.applicationContext) + } + + executorService.execute { + initializeCrashReportingSDKS() + } + } + + private fun initializeCrashReportingSDKS() { + + } + + private fun initializeAnalyticsSDKS(context: Context) { + // Initialize Firebase Analytics + firebaseAnalytics = FirebaseAnalytics.getInstance(context) + + // Example of logging an event after initialization + val bundle = Bundle().apply { + putString(FirebaseAnalytics.Param.METHOD, "app_start") + } + + /* + firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT) { + param(FirebaseAnalytics.Param.ITEM_ID, bundle); + param(FirebaseAnalytics.Param.ITEM_NAME, name); + param(FirebaseAnalytics.Param.CONTENT_TYPE, "image"); + } + */ + + firebaseAnalytics.logEvent(FirebaseAnalytics.Event.APP_OPEN, bundle) } @@ -44,9 +84,20 @@ class ShambaApp : Application() { } } + /** + * StrictMode is a developer tool which detects things you might be doing + * by accident and brings them to your attention so you can fix them + * + */ private fun setNetworkSecurity() { val builder = StrictMode.VmPolicy.Builder() - StrictMode.setVmPolicy(builder.build()) + StrictMode.setVmPolicy(builder + .detectAll() + .detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects() + .penaltyLog() + .penaltyDeath() + .build()) } private fun timber() { diff --git a/app/src/main/java/com/steve_md/smartmkulima/adapter/FarmProduceAdapter.kt b/app/src/main/java/com/steve_md/smartmkulima/adapter/FarmProduceAdapter.kt index b200ef43..aec7e801 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/adapter/FarmProduceAdapter.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/adapter/FarmProduceAdapter.kt @@ -8,10 +8,13 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.steve_md.smartmkulima.databinding.ProductsRowBinding +import com.steve_md.smartmkulima.model.AgroDealer import com.steve_md.smartmkulima.model.FarmProduce -class FarmProduceAdapter : ListAdapter(MyDiffUtil) { +class FarmProduceAdapter( + private val onClickListener: OnClickListener +) : ListAdapter(MyDiffUtil) { object MyDiffUtil : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: FarmProduce, newItem: FarmProduce): Boolean { return oldItem == newItem @@ -47,5 +50,13 @@ class FarmProduceAdapter : ListAdapter Unit) { + fun onClick(farmProduce: FarmProduce) = clickListener(farmProduce) } } \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/data/repositories/FarmCycleRepository.kt b/app/src/main/java/com/steve_md/smartmkulima/data/repositories/FarmCycleRepository.kt index 9a9186c7..25dae904 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/data/repositories/FarmCycleRepository.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/data/repositories/FarmCycleRepository.kt @@ -141,23 +141,34 @@ class FarmCycleRepository @Inject constructor( } suspend fun fieldAgentAddANewAgroDealer(fieldAgentAddAgroDealerData: FieldAgentAddAgroDealerData) = apiRequestByResource { + + // Inserting AgroDealer Into db fieldAgentAddAgrodealerDao.fieldAgentAddANewAgrodealer(fieldAgentAddAgroDealerData) - } - fun getAllFieldAgentAddedAgroDealers(): LiveData> { - return fieldAgentAddAgrodealerDao.getAllFieldAgentAddedAgrodealers() - } + // Update field agent points + val agentPoints = fieldAgentEarningsDao.getPointsByAgentId(fieldAgentAddAgroDealerData.agentId) + if (agentPoints != null) { + agentPoints.points += 10 + agentPoints.earnings += 200.0 + fieldAgentEarningsDao.saveOrUpdatePoints(agentPoints) + } else { + // First time this agent registers/ adds a new farmer + val newAgentPoints = FieldAgentEarnings( + fieldAgentID = fieldAgentAddAgroDealerData.agentId, + points = 10, + earnings = 200.0 + ) - // Field agent earnings METHODS - suspend fun saveEarnings(fieldAgentEarnings: FieldAgentEarnings) = apiRequestByResource { - fieldAgentEarningsDao.saveFieldAgentEarnings(fieldAgentEarnings) + fieldAgentEarningsDao.saveOrUpdatePoints(newAgentPoints) + } } - suspend fun updateFieldAgentEarnings(newPoints: Int, earnings: Double) { - fieldAgentEarningsDao.updateFieldAgentEarnings(newPoints, earnings) + + suspend fun getAgentPoints(agentId: String): FieldAgentEarnings? { + return fieldAgentEarningsDao.getPointsByAgentId(agentId) } - fun getAllFieldAgentEarnings(): LiveData { - return fieldAgentEarningsDao.fetchAllFieldAgentEarnings() + fun getAllFieldAgentAddedAgroDealers(): LiveData> { + return fieldAgentAddAgrodealerDao.getAllFieldAgentAddedAgrodealers() } } \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/data/room/AppDatabase.kt b/app/src/main/java/com/steve_md/smartmkulima/data/room/AppDatabase.kt index 7b0e3fa4..7f81fd30 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/data/room/AppDatabase.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/data/room/AppDatabase.kt @@ -33,7 +33,7 @@ import com.steve_md.smartmkulima.model.responses.fieldagent.Data OrderCheckoutByFarmer::class, FieldAgentAddAgroDealerData::class, Data::class, FieldAgentEarnings::class, com.steve_md.smartmkulima.model.responses.buyer.Data::class], - version = 12,exportSchema = false) + version = 13,exportSchema = false) abstract class AppDatabase : RoomDatabase() { abstract fun transactionDao(): TransactionDao diff --git a/app/src/main/java/com/steve_md/smartmkulima/data/room/FieldAgentEarningsDao.kt b/app/src/main/java/com/steve_md/smartmkulima/data/room/FieldAgentEarningsDao.kt index a735230d..34b0cd42 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/data/room/FieldAgentEarningsDao.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/data/room/FieldAgentEarningsDao.kt @@ -5,7 +5,6 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.steve_md.smartmkulima.model.OrderCheckoutByFarmer import com.steve_md.smartmkulima.model.fieldagentmodels.FieldAgentEarnings @@ -13,11 +12,9 @@ import com.steve_md.smartmkulima.model.fieldagentmodels.FieldAgentEarnings interface FieldAgentEarningsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun saveFieldAgentEarnings(fieldAgentEarnings: FieldAgentEarnings) + suspend fun saveOrUpdatePoints(fieldAgentEarnings: FieldAgentEarnings) - @Query("UPDATE field_agents_earnings SET points =:newPoints, earnings = :newEarnings") - suspend fun updateFieldAgentEarnings(newPoints: Int, newEarnings: Double) - - @Query("SELECT * FROM field_agents_earnings") - fun fetchAllFieldAgentEarnings(): LiveData + @Query("SELECT * FROM field_agents_earnings WHERE fieldAgentID = :agentId LIMIT 1") + suspend fun getPointsByAgentId(agentId: String): FieldAgentEarnings? // Only expecting a single result. + // save loads of data and improve query performance. } \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentAddAgroDealerData.kt b/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentAddAgroDealerData.kt index 18cd9085..50ec15a6 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentAddAgroDealerData.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentAddAgroDealerData.kt @@ -14,5 +14,7 @@ data class FieldAgentAddAgroDealerData( val email: String, val phone: String, val location: String, - val physicalLocationAddress: String + val physicalLocationAddress: String, + val agentId: String, // field agent who registered this farmer + val offers: String // offer categories ie. select where applicable ) diff --git a/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentEarnings.kt b/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentEarnings.kt index 339a4b8d..4f06f651 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentEarnings.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/model/fieldagentmodels/FieldAgentEarnings.kt @@ -4,10 +4,16 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey + +/** + * A model data class to track the field agent's points + * for registering / adding new farmers. + */ @Entity(tableName = "field_agents_earnings") data class FieldAgentEarnings( @PrimaryKey(autoGenerate = true) val id: Long = 0, - val points: Int = 0, - val earnings: Double = 0.0 + val fieldAgentID: String, + var points: Int = 0, + var earnings: Double = 0.0 ) \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/activities/MainActivity.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/activities/MainActivity.kt index 4105d03a..4a3446cd 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/activities/MainActivity.kt @@ -12,6 +12,7 @@ import android.os.Bundle import android.view.View import android.view.Window import android.view.WindowManager +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate @@ -19,10 +20,16 @@ import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import androidx.navigation.ui.setupWithNavController +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability import com.steve_md.smartmkulima.R import com.steve_md.smartmkulima.databinding.ActivityMainBinding import com.steve_md.smartmkulima.utils.SnackbarHelper import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber @AndroidEntryPoint class MainActivity : AppCompatActivity() { @@ -32,6 +39,8 @@ class MainActivity : AppCompatActivity() { private var screenCaptureCallback: ScreenCaptureCallback? = null + private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,6 +48,7 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + checkUpdate() supportActionBar?.hide() @@ -73,9 +83,34 @@ class MainActivity : AppCompatActivity() { } } + private val updateLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { + if (it?.data == null) return@registerForActivityResult + if (it.resultCode == UPDATE_REQUEST_CODE) { + Timber.d("Download Started") + if (it.resultCode != Activity.RESULT_OK) { + Timber.d("Download Failed") + } + } + } + /** + * we are getting the info if the update is available or not + */ + private fun checkUpdate() { + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + appUpdateManager.startUpdateFlowForResult( // to start the app update process. + appUpdateInfo, + updateLauncher, + AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()) + } + } + } - + companion object { + const val UPDATE_REQUEST_CODE = 1 + } } diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/ManualWalkingFarmMappingFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/ManualWalkingFarmMappingFragment.kt index e141aa81..f0d11022 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/ManualWalkingFarmMappingFragment.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/ManualWalkingFarmMappingFragment.kt @@ -1,8 +1,10 @@ package com.steve_md.smartmkulima.ui.fragments.main import android.Manifest +import android.content.Context import android.content.pm.PackageManager -import android.location.Criteria +import android.location.LocationListener +import android.location.LocationManager import android.os.Bundle import android.os.Looper import android.view.LayoutInflater @@ -23,25 +25,26 @@ 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.android.gms.maps.model.Polygon import com.google.android.gms.maps.model.PolygonOptions import com.google.android.gms.maps.model.PolylineOptions import com.google.maps.android.SphericalUtil +import com.google.maps.android.SphericalUtil.computeOffsetOrigin import com.steve_md.smartmkulima.R import com.steve_md.smartmkulima.databinding.FragmentManualWalkingFarmMappingBinding -import com.steve_md.smartmkulima.ui.fragments.others.LocationProvider import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber /** * Aspect of Manual Mapping by walking in the farm */ @AndroidEntryPoint -class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ - private lateinit var binding: FragmentManualWalkingFarmMappingBinding +class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback { + private lateinit var binding: FragmentManualWalkingFarmMappingBinding private lateinit var googleMap: GoogleMap - private lateinit var locationProvider: LocationProvider private lateinit var fusedLocationClient: FusedLocationProviderClient // List of Pins to be Placed to Create a Polygon @@ -49,9 +52,12 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ private var isMappingActive: Boolean = false private var farmPolygon: Polygon? = null + private lateinit var locationManager: LocationManager + private lateinit var locationListener: LocationListener + companion object { - private const val LOCATION_PERMISSION_CODE: Int = 1 + private const val LOCATION_PERMISSION_CODE: Int = 1000 } override fun onCreateView( @@ -71,6 +77,8 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext()) + //set up location manager + binding.apply { buttonStartMapping.setOnClickListener { isMappingActive = true @@ -108,6 +116,15 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ } } + + /* + private fun getPolygonCorners(startLatLng: LatLng, endLng: LatLng) { + val corners = arrayOfNulls(4) + corners[0] = computeOffsetOrigin(endLng, 12.0, 16.0) // test dummy values + } + */ + + private fun saveMapppedArea() { val calculatedFarmSize = SphericalUtil.computeArea(pathPoints) // area in Metres squared @@ -127,7 +144,7 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ this.googleMap.mapType = GoogleMap.MAP_TYPE_SATELLITE - setCriteria() + // setCriteria() // Get current location of the user. // as it zooms @@ -155,16 +172,23 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ ) } + /** + * Zoom and give the user's + * current location on the Map. + */ private fun getMyCurrentLocationANDStartManualMapping() { - locationProvider = LocationProvider(this.requireContext()) - // Zoom to current location fusedLocationClient.lastLocation.addOnSuccessListener { location -> location?.let { val currentLatLng = LatLng(location.latitude, location.longitude) - googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 18f)) // Zoom to current location + googleMap.moveCamera( + CameraUpdateFactory.newLatLngZoom( + currentLatLng, + 18f + ) + ) // Zoom to current location } } @@ -184,46 +208,72 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { - return - } - - /** - * Real time tracking - */ - val locationRequest = LocationRequest.create().apply { - interval = 5000 - fastestInterval = 2000 - priority = LocationRequest.PRIORITY_HIGH_ACCURACY - } + requestLocationPermission() + } else { - // GPS to record user movement , track and provide location updates. - // Start plotting immediately. - fusedLocationClient.requestLocationUpdates(locationRequest, object : LocationCallback() { - override fun onLocationResult(locationResult: LocationResult) { - super.onLocationResult(locationResult) - for (location in locationResult.locations) { - val latLng = LatLng(location.latitude, location.longitude) - pathPoints.add(latLng) - - polylineOptions.add(latLng) -// googleMap.clear() - googleMap.addPolyline(polylineOptions) - googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18f)) - } + /** + * Real time tracking + */ + val locationRequest = LocationRequest.create().apply { + interval = 5000 + fastestInterval = 2000 + priority = LocationRequest.PRIORITY_HIGH_ACCURACY } - }, Looper.getMainLooper()) - } else { - showMappingNotYetStartedDialog() + /** + * Geographic Information Systems. + */ + + // GPS to record user movement , track and provide location updates. + // Start plotting immediately. + fusedLocationClient.requestLocationUpdates( + locationRequest, + object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult) { + super.onLocationResult(locationResult) + for (location in locationResult.locations) { + val latLng = LatLng(location.latitude, location.longitude) + pathPoints.add(latLng) + + googleMap.addMarker(MarkerOptions().position(latLng)) + + polylineOptions.add(latLng) + googleMap.addPolyline(polylineOptions) + + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18f)) + + updatePolygonArea() + } + } + }, + Looper.getMainLooper() + ) + } + } else { + showMappingNotYetStartedDialog() + } + } + + + private fun updatePolygonArea() { + if (pathPoints.size > 2) { + val areaInSquareMeters = SphericalUtil.computeArea(pathPoints) + val areaInHectares = areaInSquareMeters / 10000 + + // Display the area in real time + binding.buttonSaveMappedArea.text = "Area: %.2f ha".format(areaInHectares) } } + /** * Set map location criteria to follow while setting up * location updates */ + + /* private fun setCriteria() { // Location criteria val locationCriteria : Criteria = Criteria() @@ -237,6 +287,8 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ locationCriteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH) } + */ + private fun showMappingNotYetStartedDialog() { AlertDialog.Builder(requireContext()) .setTitle("Mapping Has Not Started") @@ -255,6 +307,45 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ } + private fun setupLocationManager() { + + locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) + as LocationManager + + locationListener = LocationListener { location -> + // Handle new location + val latitude = location.latitude + val longitude = location.longitude + + // Update your UI or send location data to a server + Timber.d("Current Location: $latitude || $longitude") + } + + // Check for the appropriate location provider and request updates + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + + locationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + 5000L, // Time in milliseconds between location updates + 10f, // Distance in meters between location updates + locationListener + ) + + // permissions granted proceed with location updates. + setupLocationManager() + + } else { + requestLocationPermission() + setupLocationManager() + } + } private fun resetMapping() { farmPolygon?.remove() @@ -263,7 +354,7 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ } private fun createPolygon() { - if (pathPoints.size >= 2) { + if (pathPoints.size > 0) { farmPolygon = googleMap.addPolygon( PolygonOptions() .addAll(pathPoints) @@ -278,15 +369,12 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ // Display the area binding.buttonSaveMappedArea.text = "Save Mapped Area: %.2f ha".format(areaInHectares) + Timber.d("Mapped $areaInHectares ha") - } else { + } + else { binding.buttonSaveMappedArea.text = "Mapped Area: 0.0 ha" - // Show a message if the user hasn't marked enough points to create a polygon - AlertDialog.Builder(requireContext()) - .setTitle("Insufficient Path Points") - .setMessage("Please mark at least three points to create a farm boundary.") - .setPositiveButton("OK", null) - .show() + Timber.d("Mapped 0.0ha") } } @@ -299,6 +387,8 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ if (requestCode == LOCATION_PERMISSION_CODE) { if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { getMyCurrentLocationANDStartManualMapping() + // permissions granted proceed with location updates. + setupLocationManager() } else { AlertDialog.Builder(requireContext()) .setTitle("Permission Denied") @@ -309,6 +399,14 @@ class ManualWalkingFarmMappingFragment : Fragment() ,OnMapReadyCallback{ } } + override fun onPause() { + super.onPause() + if (::locationManager.isInitialized) { + locationManager.removeUpdates(locationListener) + } + } + + /** * Lifecycle callbacks. */ diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MappingFarmLocationWithPinsFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MappingFarmLocationWithPinsFragment.kt index 09edfefa..8c11d49a 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MappingFarmLocationWithPinsFragment.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MappingFarmLocationWithPinsFragment.kt @@ -125,8 +125,6 @@ class MappingFarmLocationWithPinsFragment : Fragment() ,OnMapReadyCallback { // Continue to AddNewFarmFieldFragment findNavController().navigate(R.id.addNewFarmFieldFragment, bundle) - findNavController().popBackStack() - } private fun showMappingInProgressDialog() { @@ -184,7 +182,7 @@ class MappingFarmLocationWithPinsFragment : Fragment() ,OnMapReadyCallback { ) } - userLocationLatLng?.let { CameraUpdateFactory.newLatLngZoom(it,15f) } + userLocationLatLng?.let { CameraUpdateFactory.newLatLngZoom(it,18f) } ?.let { map.moveCamera(it) } // Then click on the map and start mapping @@ -208,7 +206,7 @@ class MappingFarmLocationWithPinsFragment : Fragment() ,OnMapReadyCallback { private fun updateFarmPolygon() { farmPolygon?.remove() - if (boundaryPoints.size >= 3) { + if (boundaryPoints.size > 0) { val polygonOptions = PolygonOptions() .addAll(boundaryPoints) .strokeWidth(2f) diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/agrodealer/AgroDealerHomeDashboardFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/agrodealer/AgroDealerHomeDashboardFragment.kt index fc88c13d..72b74780 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/agrodealer/AgroDealerHomeDashboardFragment.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/agrodealer/AgroDealerHomeDashboardFragment.kt @@ -30,6 +30,7 @@ class AgroDealerHomeDashboardFragment : Fragment() { private val viewModel: MainViewModel by viewModels() private lateinit var agrodealerOrdersAdapter : AgrodealerOrdersAdapter private var ordersList = mutableListOf() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -101,7 +102,7 @@ class AgroDealerHomeDashboardFragment : Fragment() { binding.recyclerView5.layoutManager = LinearLayoutManager(requireContext()) // Initialize the adapter - agrodealerOrdersAdapter = AgrodealerOrdersAdapter(AgrodealerOrdersAdapter.OnClickListener{ order -> + agrodealerOrdersAdapter = AgrodealerOrdersAdapter( AgrodealerOrdersAdapter.OnClickListener { order -> Timber.tag("...CreatedFarmCycles....").e(order.toString()) Timber.i("=====Checking==ORDER WITH ID=====>: ${order.orderId} ") diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MarketProduce.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduce.kt similarity index 87% rename from app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MarketProduce.kt rename to app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduce.kt index 89787ce7..aaa1c2ce 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/MarketProduce.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduce.kt @@ -1,4 +1,4 @@ -package com.steve_md.smartmkulima.ui.fragments.main +package com.steve_md.smartmkulima.ui.fragments.main.buyer import android.os.Bundle import android.text.Editable @@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import com.steve_md.smartmkulima.R import com.steve_md.smartmkulima.adapter.FarmProduceAdapter import com.steve_md.smartmkulima.databinding.FragmentMarketProduceBinding import com.steve_md.smartmkulima.model.FarmProduce @@ -31,7 +32,7 @@ class MarketProduce : Fragment() { private lateinit var binding: FragmentMarketProduceBinding private val mainViewModel by viewModels() - private val farmProduceAdapter by lazy { FarmProduceAdapter() } + private lateinit var farmProduceAdapter: FarmProduceAdapter private val farmProduceList = mutableListOf() @@ -44,20 +45,33 @@ class MarketProduce : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + (activity as AppCompatActivity).supportActionBar?.hide() subScribeToFarmProduceObserver() setUpBinding() - } - private fun setUpBinding() { + farmProduceAdapter = + FarmProduceAdapter(FarmProduceAdapter.OnClickListener { farmProduce -> + Timber.i("Farm Produce ::Product:: ${farmProduce.productTitle}") + + val bundle = Bundle().apply { + putString("productImage", farmProduce.productImageUrl) + putString("productTitle", farmProduce.productTitle) + putString("productDescription", farmProduce.productPrice) + } + findNavController().navigate(R.id.marketProduceInDetailsFragment, bundle) + }) + } + private fun setUpBinding() { + // Filters Search results as you type in the Edit Text binding.inputSearchFarmProduce.addTextChangedListener( object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (s.isNullOrEmpty()) { @@ -68,7 +82,6 @@ class MarketProduce : Fragment() { searchingFarmProduce(s.toString()) } } - override fun afterTextChanged(s: Editable?) { } diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduceInDetailsFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduceInDetailsFragment.kt new file mode 100644 index 00000000..b9969e7e --- /dev/null +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/buyer/MarketProduceInDetailsFragment.kt @@ -0,0 +1,95 @@ +package com.steve_md.smartmkulima.ui.fragments.main.buyer + +import android.annotation.SuppressLint +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.bumptech.glide.Glide +import com.steve_md.smartmkulima.databinding.FragmentMarketProduceInDetailsBinding +import com.steve_md.smartmkulima.utils.OverlayService +import com.steve_md.smartmkulima.utils.displaySnackBar +import com.steve_md.smartmkulima.utils.hideSupportActionBar +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MarketProduceInDetailsFragment : Fragment() { + private lateinit var binding: FragmentMarketProduceInDetailsBinding + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentMarketProduceInDetailsBinding.inflate( + inflater, container, false + ) + return binding.root + } + + @SuppressLint("SetTextI18n") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + hideSupportActionBar() + + Glide.with(binding.imageView32) + .load(arguments?.getString("productImage")) + .centerCrop() + .into(binding.imageView32) + binding.textView175.text = arguments?.getString("productTitle") + binding.textView176.text = ""+arguments?.getString("productDescription") + + binding.addToCart.setOnClickListener { + // check permission for overlay over other apps. + checkOverlayPermission() + } + } + + private fun checkOverlayPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!Settings.canDrawOverlays(requireContext())) { + // Permission is not granted, request it + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${requireContext().packageName}")) + + startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION) + } else { + // Permission is granted, show the overlay + showOverLay() + } + } else { + // For devices below API 23, permission is granted by default + showOverLay() + } + } + + private fun showOverLay() { + val intent = Intent(requireContext(), OverlayService::class.java) + requireContext().startService(intent) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (Settings.canDrawOverlays(requireContext())) { + // Permission is granted + showOverLay() + + displaySnackBar("Permitted to overlay over other apps") + } else { + displaySnackBar("No permission to overlay over other apps") + } + } + } + } + + + + companion object { + const val REQUEST_CODE_OVERLAY_PERMISSION = 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/AddANewAgroDealerBottomSheetFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/AddANewAgroDealerBottomSheetFragment.kt index 95e419ef..a3aa4d11 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/AddANewAgroDealerBottomSheetFragment.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/AddANewAgroDealerBottomSheetFragment.kt @@ -34,10 +34,6 @@ class AddANewAgroDealerBottomSheetFragment : BottomSheetDialogFragment() { private lateinit var binding: FragmentAddANewAgroDealerBottomsheetBinding private lateinit var fusedLocationClient: FusedLocationProviderClient private val viewModel: MainViewModel by viewModels() - private var fieldAgentEarning: FieldAgentEarnings? = null - private val agentId: Long = 1 // By Default say agent ID is 1 - private val pointsPerFarmer = 10 - private val pointsToKSHRate = 200.0 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -52,6 +48,8 @@ class AddANewAgroDealerBottomSheetFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + Timber.tag("-on View Created - ShowAddAgrodealerBottomSheet 2").d("Received arg is: ${arguments?.getString("fieldAgentEmail")}") + fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity()) binding.inputAgroDealerLocation.setOnFocusChangeListener { _, hasFocus -> @@ -64,45 +62,23 @@ class AddANewAgroDealerBottomSheetFragment : BottomSheetDialogFragment() { } } - - binding.addAgroDealerByFieldAgentBtn.setOnClickListener { - if (inputsAreValidated()) { + if (inputsAreValidated()) { val fieldAgentAddAgroDealerData = FieldAgentAddAgroDealerData( - name = binding.inputAgroDealerName.text.toString(), - email = binding.inputAgroDealerEmail.text.toString(), - phone = binding.inputAgroDealerPhone.text.toString(), - location = binding.inputAgroDealerLocation.text.toString(), - physicalLocationAddress = binding.inputAgroDealerPhysicalAddress.text.toString() - ) + name = binding.inputAgroDealerName.text.toString(), + email = binding.inputAgroDealerEmail.text.toString(), + phone = binding.inputAgroDealerPhone.text.toString(), + location = binding.inputAgroDealerLocation.text.toString(), + physicalLocationAddress = binding.inputAgroDealerPhysicalAddress.text.toString(), + offers = binding.spinnerSelectOfferCategories.selectedItem.toString(), + agentId = "${arguments?.getString("fieldAgentEmail")}" + ) + try { viewModel.fieldAgentAddANewAgroDealer(fieldAgentAddAgroDealerData) - viewModel.allFieldAgentEarnings.observe(viewLifecycleOwner) { - if (it.earnings == 200.0 && it.points == 10) { - - val newPoints = it.points + pointsPerFarmer - - val newEarnings = calculateEarnings(newPoints) - - viewModel.updateFieldAgentEarnings( - newPoints, newEarnings - ) - } else { - viewModel.saveFieldAgentEarnings( - FieldAgentEarnings( - 1, 10, 200.0 - ) - ) - } - } - - - - - requireActivity().runOnUiThread { displaySnackBar("A new Agro-dealer was added.") } @@ -117,28 +93,6 @@ class AddANewAgroDealerBottomSheetFragment : BottomSheetDialogFragment() { } - updateFieldAgentEarnings() - } - - private fun updateFieldAgentEarnings() { - lifecycleScope.launch { - fieldAgentEarning?.let { - val newPoints = it.points + pointsPerFarmer - val newEarnings = calculateEarnings(newPoints) - - // Update earnings and points in room db - viewModel.updateFieldAgentEarnings( - newPoints, newEarnings - ) - - toast("Farmer added! You've earned 10 points. Yaaay!") - } - } - } - - - private fun calculateEarnings(points: Int): Double { - return points * (pointsToKSHRate / pointsPerFarmer) } private fun getCurrentLocation(onLocationReceived: (Location) -> Unit) { @@ -186,8 +140,10 @@ class AddANewAgroDealerBottomSheetFragment : BottomSheetDialogFragment() { } } - companion object { const val LOCATION_PERMISSION_REQUEST_CODE = 1000 + const val TAG = "AddNewAgroDealerBottomSheeet" } + + } \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/FieldAgentDashboardFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/FieldAgentDashboardFragment.kt index 3e2bbbd3..6a710e48 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/FieldAgentDashboardFragment.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/fieldagents/FieldAgentDashboardFragment.kt @@ -3,6 +3,8 @@ package com.steve_md.smartmkulima.ui.fragments.main.fieldagents import android.graphics.drawable.GradientDrawable.Orientation import android.icu.lang.UCharacter.VerticalOrientation import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View @@ -35,6 +37,7 @@ import com.steve_md.smartmkulima.viewmodel.MainViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import timber.log.Timber import java.util.Calendar @AndroidEntryPoint @@ -97,6 +100,10 @@ class FieldAgentDashboardFragment : Fragment() { setUpRecyclerView() fetchAllFieldAgentCreatedAgroDealers() + + viewModel.getAgentPoints("${arguments?.getString("fieldAgentEmail")}") + + subscribeToFieldAgentEarningVMObservables() } @@ -123,13 +130,14 @@ class FieldAgentDashboardFragment : Fragment() { private fun setUpUi() { - subscribeToFieldAgentEarningVMObservables() - binding.apply { + val agentEmail = arguments?.getString("fieldAgentEmail") button4.setOnClickListener { - showAddANewAgroDealerBottomSheetFragment() + if (agentEmail != null) { + showAddANewAgroDealerBottomSheetFragment(agentEmail) + } } button3.setOnClickListener { @@ -137,11 +145,35 @@ class FieldAgentDashboardFragment : Fragment() { progressBar7.isVisible = true delay(500L) progressBar7.isVisible = false + // Load agent points initially + viewModel.getAgentPoints("${arguments?.getString("fieldAgentEmail")}") } } } + + binding.inputSearchAgrodealer.addTextChangedListener( object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + if (s.isNullOrEmpty()) { + binding.noRecordsAgrodealers.isVisible = false + fieldAgentAddedAgroDealersAdapter.submitList(fieldAgentAgroDealers) + binding.recyclerViewAddedAgrodealers.isVisible = true // when no search rlts + } else { + searchingAgroDealer(s.toString()) + } + } + + override fun afterTextChanged(s: Editable?) { + + } + }) + binding.inputSearchAgrodealer.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_SEARCH) { @@ -172,20 +204,48 @@ class FieldAgentDashboardFragment : Fragment() { } private fun subscribeToFieldAgentEarningVMObservables() { - viewModel.allFieldAgentEarnings.observe(viewLifecycleOwner) { + viewModel.agentPoints.observe(viewLifecycleOwner) { it?.let { binding.textViewEarnings.text = "Current Earnings: ${it.points} points worth KSH. ${it.earnings}" } } } - private fun showAddANewAgroDealerBottomSheetFragment() { - val modal = AddANewAgroDealerBottomSheetFragment() - modal.show(parentFragmentManager, TAG) + private fun showAddANewAgroDealerBottomSheetFragment(fieldAgentEmail: String) { + // pass here field agent ID here as an arg who registered / added this new farmer + +// val bottomSheet = AddANewAgroDealerBottomSheetFragment.newInstance(agentEmail) +// bottomSheet.show(parentFragmentManager, bottomSheet.tag) + + val modal = AddANewAgroDealerBottomSheetFragment().apply { + arguments = Bundle().apply { + putString("fieldAgentEmail", fieldAgentEmail) + } + } + // modal.show(parentFragmentManager, TAG) + parentFragmentManager.let { + modal.show(it, AddANewAgroDealerBottomSheetFragment.TAG) + } + + Timber.tag("ShowAddAgrodealerBottomSheet").d("Passed arg is: ${arguments?.getString("fieldAgentEmail")}") + +// val fragment = AddANewAgroDealerBottomSheetFragment() +// val bundle = Bundle().apply { +// putString("agentEmail","${arguments?.getString("fieldAgentEmail")}") +// } +// fragment.arguments = bundle +// fragment.show(parentFragmentManager, fragment.tag) + } + + private fun searchingAgroDealer(searchText: String) { - val filteredList = fieldAgentAgroDealers.filter { it.name.equals(searchText, ignoreCase = true) } + val filteredList = fieldAgentAgroDealers.filter { + it.name.contains(searchText, ignoreCase = true) + } fieldAgentAddedAgroDealersAdapter.submitList(filteredList.toMutableList()) + + binding.noRecordsAgrodealers.isVisible = filteredList.isEmpty() } private fun exitDialog() { @@ -214,9 +274,25 @@ class FieldAgentDashboardFragment : Fragment() { } override fun onResume() { super.onResume() + + viewModel.getAgentPoints("${arguments?.getString("fieldAgentEmail")}") + displaySnackBar("Yaaay! Onboard more Agro-Dealers to increase your earnings") } companion object { const val TAG = "AddANewAgroDealerBottomSheetFragment" + + + private const val ARG_AGENT_EMAIL = "agentEmail" + + // Create new instance of BottomSheet with argument + fun newInstance(agentEmail: String): AddANewAgroDealerBottomSheetFragment { + val fragment = AddANewAgroDealerBottomSheetFragment() + val args = Bundle().apply { + putString(ARG_AGENT_EMAIL, agentEmail) + } + fragment.arguments = args + return fragment + } } } \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/others/LocationProvider.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/others/LocationProvider.kt index 728acfdd..dcdd22d7 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/others/LocationProvider.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/others/LocationProvider.kt @@ -30,7 +30,7 @@ class LocationProvider(private val context:Context) { Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { - // promptUserRequestingPermissions() + promptUserRequestingPermissions() return } diff --git a/app/src/main/java/com/steve_md/smartmkulima/utils/ExtensionFunctions.kt b/app/src/main/java/com/steve_md/smartmkulima/utils/ExtensionFunctions.kt index f6c1252d..8572650f 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/utils/ExtensionFunctions.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/utils/ExtensionFunctions.kt @@ -2,16 +2,19 @@ package com.steve_md.smartmkulima.utils import android.app.Activity import android.content.Context +import android.content.Intent import android.graphics.Color import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.os.Build +import android.provider.Settings import android.view.View import android.view.WindowManager import android.view.inputmethod.InputMethodManager import android.widget.FrameLayout import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat.startActivityForResult import androidx.core.view.updateMargins import androidx.fragment.app.Fragment import com.google.android.material.snackbar.Snackbar @@ -21,6 +24,8 @@ import okhttp3.ResponseBody import org.json.JSONException import org.json.JSONObject import retrofit2.HttpException +import android.net.Uri + fun Fragment.toast(text:String) { @@ -187,3 +192,5 @@ fun formatNameFromEmail(email: String): String { + + diff --git a/app/src/main/java/com/steve_md/smartmkulima/utils/OverLayService.kt b/app/src/main/java/com/steve_md/smartmkulima/utils/OverLayService.kt new file mode 100644 index 00000000..88c5b634 --- /dev/null +++ b/app/src/main/java/com/steve_md/smartmkulima/utils/OverLayService.kt @@ -0,0 +1,63 @@ +package com.steve_md.smartmkulima.utils + + +import android.app.Service +import android.content.Intent +import android.graphics.PixelFormat +import android.os.Build +import android.os.IBinder +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import androidx.annotation.RequiresApi +import com.steve_md.smartmkulima.R + +class OverlayService : Service() { + + private lateinit var windowManager: WindowManager + private lateinit var overlayView: View + + @RequiresApi(Build.VERSION_CODES.O) + override fun onCreate() { + super.onCreate() + + // Inflate your overlay layout + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + overlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null) + + val params = WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // Use TYPE_PHONE for older versions + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT + ) + + // Specify the overlay position on the screen + params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL + params.x = 0 + params.y = 100 + + // Add the view to the window + windowManager.addView(overlayView, params) + + // Set up click listeners for the overlay + overlayView.setOnClickListener { + // Handle clicks on the overlay + stopSelf() // This will remove the overlay + } + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onDestroy() { + super.onDestroy() + // Remove the overlay view + if (::overlayView.isInitialized) { + windowManager.removeView(overlayView) + } + } +} diff --git a/app/src/main/java/com/steve_md/smartmkulima/utils/services/LocationPermissionHelper.kt b/app/src/main/java/com/steve_md/smartmkulima/utils/services/LocationPermissionHelper.kt new file mode 100644 index 00000000..2874d3bc --- /dev/null +++ b/app/src/main/java/com/steve_md/smartmkulima/utils/services/LocationPermissionHelper.kt @@ -0,0 +1,41 @@ +package com.steve_md.smartmkulima.utils.services + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.provider.Settings +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat + + +/** + * Handling Location runtime permissions in Android | Kotlin. + */ +object LocationPermissionHelper { + private val BASIC_PERMISSION = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) + private const val BASIC_PERMISSION_REQUESTCODE = 0 + + fun hasAccessFinePermission(activity: Activity): Boolean { + return ContextCompat.checkSelfPermission( + activity, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + + fun requestFineLocationPermission(activity: Activity?) { + ActivityCompat.requestPermissions(activity!!, BASIC_PERMISSION, BASIC_PERMISSION_REQUESTCODE) + } + + fun shouldShowRequestPermissionRationale(activity: Activity): Boolean{ + return ActivityCompat.shouldShowRequestPermissionRationale(activity, BASIC_PERMISSION.get(0)) + } + fun launchPermissionSettings(activity: Activity) { + val intent = Intent() + intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + intent.data = Uri.fromParts("package", activity.packageName, null) + activity.startActivity(intent) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/steve_md/smartmkulima/viewmodel/MainViewModel.kt b/app/src/main/java/com/steve_md/smartmkulima/viewmodel/MainViewModel.kt index 65168d31..706015ce 100644 --- a/app/src/main/java/com/steve_md/smartmkulima/viewmodel/MainViewModel.kt +++ b/app/src/main/java/com/steve_md/smartmkulima/viewmodel/MainViewModel.kt @@ -482,19 +482,18 @@ class MainViewModel @Inject constructor( // FIELD AGENT EARNINGS - fun updateFieldAgentEarnings(newPoints: Int, newEarnings: Double) = viewModelScope.launch { - repository.updateFieldAgentEarnings(newPoints, newEarnings) - } + private val _agentPoints = MutableLiveData() + val agentPoints: LiveData get() = _agentPoints - fun saveFieldAgentEarnings(fieldAgentEarnings: FieldAgentEarnings) = viewModelScope.launch { - withContext(Dispatchers.IO) { - repository.saveEarnings(fieldAgentEarnings) + fun getAgentPoints(agentId: String) { + viewModelScope.launch { + val points = repository.getAgentPoints(agentId) + points?.let { + _agentPoints.postValue(it) + } } } - val allFieldAgentEarnings: LiveData = - repository.getAllFieldAgentEarnings() - // Field Agents account register and log in by finding if exists in backend private var _fieldAgentRegisterState = MutableStateFlow?>(null) diff --git a/app/src/main/res/layout/fragment_add_a_new_agro_dealer_bottomsheet.xml b/app/src/main/res/layout/fragment_add_a_new_agro_dealer_bottomsheet.xml index ff9b110f..6c428f39 100644 --- a/app/src/main/res/layout/fragment_add_a_new_agro_dealer_bottomsheet.xml +++ b/app/src/main/res/layout/fragment_add_a_new_agro_dealer_bottomsheet.xml @@ -178,12 +178,25 @@ android:inputType="text|number" /> + +