From 128ff6cfaf200d47fce2ba91f08100c5daec810b Mon Sep 17 00:00:00 2001 From: Aditya-gupta99 Date: Fri, 21 Jun 2024 00:37:43 +0530 Subject: [PATCH] refactor: refactor path tracking to compose --- feature/path-tracking/build.gradle.kts | 3 + .../path_tracking/PathTrackingScreen.kt | 176 +++++++++--- .../path_tracking/PathTrackingViewModel.kt | 21 +- .../pathtracking/PathTrackingActivity.kt | 268 ++---------------- 4 files changed, 191 insertions(+), 277 deletions(-) diff --git a/feature/path-tracking/build.gradle.kts b/feature/path-tracking/build.gradle.kts index bc6299e5001..b6051372ef6 100644 --- a/feature/path-tracking/build.gradle.kts +++ b/feature/path-tracking/build.gradle.kts @@ -11,6 +11,9 @@ android { dependencies { implementation(projects.core.domain) + implementation(libs.androidx.material) + implementation(libs.accompanist.permission) + implementation(libs.coil.kt.compose) testImplementation(libs.hilt.android.testing) testImplementation(projects.core.testing) diff --git a/feature/path-tracking/src/main/java/com/mifos/feature/path_tracking/PathTrackingScreen.kt b/feature/path-tracking/src/main/java/com/mifos/feature/path_tracking/PathTrackingScreen.kt index b39314f61d9..be9d12939fe 100644 --- a/feature/path-tracking/src/main/java/com/mifos/feature/path_tracking/PathTrackingScreen.kt +++ b/feature/path-tracking/src/main/java/com/mifos/feature/path_tracking/PathTrackingScreen.kt @@ -1,17 +1,30 @@ -@file:OptIn(ExperimentalMaterial3Api::class) +@file:OptIn( + ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class, + ExperimentalPermissionsApi::class +) package com.mifos.feature.path_tracking +import android.Manifest +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBackIosNew import androidx.compose.material.icons.rounded.MyLocation import androidx.compose.material.icons.rounded.Stop +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -22,13 +35,14 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontStyle @@ -39,42 +53,82 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.registerReceiver import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.google.accompanist.permissions.rememberMultiplePermissionsState import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.LatLng +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.google.maps.android.compose.GoogleMap import com.google.maps.android.compose.MapUiSettings import com.google.maps.android.compose.rememberCameraPositionState -import com.mifos.core.datastore.PrefManager +import com.mifos.core.common.utils.Constants import com.mifos.core.designsystem.component.MifosCircularProgress import com.mifos.core.designsystem.component.MifosScaffold import com.mifos.core.designsystem.component.MifosSweetError import com.mifos.core.designsystem.theme.Black import com.mifos.core.designsystem.theme.White +import com.mifos.core.objects.user.UserLatLng import com.mifos.core.objects.user.UserLocation @Composable fun PathTrackingScreen( - userId : Int, + userId: Int, onBackPressed: () -> Unit, - onPathTrackingClick: () -> Unit + onPathTrackingClick: (List) -> Unit ) { + val context = LocalContext.current val viewModel: PathTrackingViewModel = hiltViewModel() val state by viewModel.pathTrackingUiState.collectAsStateWithLifecycle() + val refreshState by viewModel.isRefreshing.collectAsStateWithLifecycle() + val userStatus by viewModel.userStatus.collectAsStateWithLifecycle() + + DisposableEffect(Unit) { + + val notificationReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (Constants.STOP_TRACKING == action) { + viewModel.loadPathTracking(userId) + } + } + } + registerReceiver( + context, + notificationReceiver, + IntentFilter(Constants.STOP_TRACKING), + ContextCompat.RECEIVER_NOT_EXPORTED + ) + + onDispose { + context.unregisterReceiver(notificationReceiver) + } + } LaunchedEffect(Unit) { viewModel.loadPathTracking(userId) } PathTrackingScreen( - state = PathTrackingUiState.PathTracking(samplePathTrackingList), + state = state, onBackPressed = onBackPressed, - onRetry = {}, - onPathTrackingClick = onPathTrackingClick + onRetry = { + viewModel.loadPathTracking(userId) + }, + onPathTrackingClick = onPathTrackingClick, + onRefresh = { + viewModel.refreshCenterList(userId) + }, + refreshState = refreshState, + userStatus = userStatus, + updateUserStatus = { viewModel.updateUserStatus(it) } ) - } @Composable @@ -82,11 +136,24 @@ fun PathTrackingScreen( state: PathTrackingUiState, onBackPressed: () -> Unit, onRetry: () -> Unit, - onPathTrackingClick: () -> Unit + onPathTrackingClick: (List) -> Unit, + onRefresh: () -> Unit, + refreshState: Boolean, + userStatus: Boolean, + updateUserStatus: (Boolean) -> Unit ) { val snackbarHostState = remember { SnackbarHostState() } - var locateLocation by rememberSaveable { mutableStateOf(false) } + val pullRefreshState = rememberPullRefreshState( + refreshing = refreshState, + onRefresh = onRefresh + ) + val permissionState = rememberMultiplePermissionsState( + permissions = listOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) MifosScaffold( topBar = { @@ -117,10 +184,26 @@ fun PathTrackingScreen( }, actions = { IconButton( - onClick = { locateLocation = locateLocation.not() } + onClick = { + if (userStatus) { + // TODO stop Path Service + updateUserStatus(false) + } else { + permissionState.permissions.all { per -> + per.status.isGranted + }.let { allGranted -> + if (allGranted) { + // TODO Run Path Service + updateUserStatus(true) + } else { + permissionState.launchMultiplePermissionRequest() + } + } + } + } ) { Icon( - imageVector = if (locateLocation) Icons.Rounded.Stop else Icons.Rounded.MyLocation, + imageVector = if (userStatus) Icons.Rounded.Stop else Icons.Rounded.MyLocation, contentDescription = null, ) } @@ -130,21 +213,28 @@ fun PathTrackingScreen( snackbarHostState = snackbarHostState ) { paddingValues -> Column(modifier = Modifier.padding(paddingValues)) { - when (state) { - is PathTrackingUiState.Error -> { - MifosSweetError(message = stringResource(id = state.message)) { - onRetry() + Box(modifier = Modifier.pullRefresh(pullRefreshState)) { + when (state) { + is PathTrackingUiState.Error -> { + MifosSweetError(message = stringResource(id = state.message)) { + onRetry() + } } - } - is PathTrackingUiState.Loading -> MifosCircularProgress() + is PathTrackingUiState.Loading -> MifosCircularProgress() - is PathTrackingUiState.PathTracking -> { - PathTrackingContent( - pathTrackingList = state.userLocations, - onPathTrackingClick = onPathTrackingClick - ) + is PathTrackingUiState.PathTracking -> { + PathTrackingContent( + pathTrackingList = state.userLocations, + onPathTrackingClick = onPathTrackingClick + ) + } } + PullRefreshIndicator( + refreshing = refreshState, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) } } } @@ -154,7 +244,7 @@ fun PathTrackingScreen( @Composable fun PathTrackingContent( pathTrackingList: List, - onPathTrackingClick: () -> Unit + onPathTrackingClick: (List) -> Unit ) { LazyColumn { items(pathTrackingList) { pathTracking -> @@ -169,13 +259,14 @@ fun PathTrackingContent( @Composable fun PathTrackingItem( pathTracking: UserLocation, - onPathTrackingClick: () -> Unit + onPathTrackingClick: (List) -> Unit ) { - val testLatLng = LatLng(12.905150682069308, 77.5651237271759) + val latLngList = getLatLngList(pathTracking.latlng) + val latLng = latLngList[0] val cameraPositionState = rememberCameraPositionState { - position = CameraPosition.fromLatLngZoom(testLatLng, 15f) + position = CameraPosition.fromLatLngZoom(LatLng(latLng.lat, latLng.lng), 15f) } - var uiSettings by remember { + val uiSettings by remember { mutableStateOf(MapUiSettings(zoomControlsEnabled = false)) } @@ -183,7 +274,7 @@ fun PathTrackingItem( modifier = Modifier .padding(8.dp), onClick = { - onPathTrackingClick() + onPathTrackingClick(latLngList) }, colors = CardDefaults.outlinedCardColors(White) ) { @@ -207,6 +298,14 @@ fun PathTrackingItem( } } +private fun getLatLngList(latLngString: String?): List { + val gson = Gson() + return gson.fromJson( + latLngString, + object : TypeToken>() {}.type + ) +} + class PathTrackingUiStateProvider : PreviewParameterProvider { @@ -217,7 +316,6 @@ class PathTrackingUiStateProvider : PreviewParameterProvider(PathTrackingUiState.Loading) val pathTrackingUiState = _pathTrackingUiState.asStateFlow() + private val _isRefreshing = MutableStateFlow(false) + val isRefreshing = _isRefreshing.asStateFlow() + + private val _userStatus = MutableStateFlow(prefManager.userStatus) + val userStatus = _userStatus.asStateFlow() + + fun refreshCenterList(userId: Int) { + _isRefreshing.value = true + loadPathTracking(userId) + _isRefreshing.value = false + } + fun loadPathTracking(userId: Int) = viewModelScope.launch(Dispatchers.IO) { getUserPathTrackingUseCase(userId).collect { result -> @@ -41,4 +55,9 @@ class PathTrackingViewModel @Inject constructor( } } + fun updateUserStatus(status: Boolean) = viewModelScope.launch(Dispatchers.IO) { + prefManager.userStatus = status + _userStatus.value = status + } + } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/activity/pathtracking/PathTrackingActivity.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/activity/pathtracking/PathTrackingActivity.kt index b2805706fb9..da1bf7fe2ef 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/activity/pathtracking/PathTrackingActivity.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/activity/pathtracking/PathTrackingActivity.kt @@ -4,9 +4,13 @@ */ package com.mifos.mifosxdroid.activity.pathtracking +import android.content.Intent +import android.net.Uri import android.os.Bundle import androidx.activity.compose.setContent +import com.mifos.core.objects.user.UserLatLng import com.mifos.feature.path_tracking.PathTrackingScreen +import com.mifos.mifosxdroid.R import com.mifos.mifosxdroid.core.MifosBaseActivity import com.mifos.utils.PrefManager import dagger.hilt.android.AndroidEntryPoint @@ -15,256 +19,36 @@ import dagger.hilt.android.AndroidEntryPoint * @author fomenkoo */ @AndroidEntryPoint -class PathTrackingActivity : MifosBaseActivity() -// , OnRefreshListener -{ +class PathTrackingActivity : MifosBaseActivity() { + - // private lateinit var binding: ActivityPathTrackerBinding -// -// private lateinit var viewModel: PathTrackingViewModel -// -// private var pathTrackingAdapter: PathTrackingAdapter? = null -// private var intentLocationService: Intent? = null -// private var notificationReceiver: BroadcastReceiver? = null -// private var userLocations: List? = null -// private var sweetUIErrorHandler: SweetUIErrorHandler? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PathTrackingScreen( userId = PrefManager.getUserId(), onBackPressed = { finish() }, - onPathTrackingClick = {} + onPathTrackingClick = { userLatLngs -> + onUserLocationClick(userLatLngs) + } ) } -// binding = ActivityPathTrackerBinding.inflate(layoutInflater) -// setContentView(binding.root) -// viewModel = ViewModelProvider(this)[PathTrackingViewModel::class.java] -// showBackButton() -// intentLocationService = Intent(this, PathTrackingService::class.java) -// createNotificationReceiver() -// showUserInterface() -// viewModel.loadPathTracking(PrefManager.getUserId()) -// binding.layoutError.findViewById