From 5530f587f0665fd299373a82e9b7db597eb0da87 Mon Sep 17 00:00:00 2001 From: Aditya_WDI Date: Thu, 28 Sep 2023 18:06:10 +0530 Subject: [PATCH] . --- app/src/main/AndroidManifest.xml | 3 + .../com/ssb/simplitend/apputils/AppUtil.java | 267 ++++++++++++------ .../apputils/NotificationService.java | 64 ++++- .../activities/CaregiverDashActivity.java | 2 + .../fragments/CgDashBoardFragment.java | 19 +- .../fragments/MyPatientFragment.java | 3 +- .../mvvm/CaregiverMainViewModel.java | 5 +- .../mvvm/CgDashboardApiService.java | 6 +- .../mvvm/CgHomeRepository.java | 77 ++--- .../mvvm/NotificationApiService.java | 18 ++ .../mvvm/models/GeoFenceDetails.java | 15 + .../cg_geofencing/CgGeoFencingActivity.java | 1 + .../patient_dashboard/DashBoardActivity.java | 85 +++++- .../PatientMainViewModel.java | 107 +++++++ .../GeoFenceBroadcastReceiver.java | 77 +++++ .../patientgeofencing/GeoFenceHelper.java | 63 +++++ 16 files changed, 672 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/NotificationApiService.java create mode 100644 app/src/main/java/com/ssb/simplitend/patient_dashboard/PatientMainViewModel.java create mode 100644 app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceBroadcastReceiver.java create mode 100644 app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceHelper.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05b941e..7c5e32e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + + + appList = mySharedPref.getArrayList("APP_LIST"); - if (appList != null) { - appList.clear(); - mySharedPref.setArrayList("APP_LIST", appList); - } - } - - public static void saveCgData(String token, int patient_id, Context context){ - SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - - editor.putString(CAREGIVER_TOKEN, token); - - editor.apply(); - - // now saving patient data - // This is important as there are instances where patient data such as patient id and patient_token is required - savePatientData(token, patient_id, context, false); - - Log.d(TAG, "saveToken: caregiver token saved successful"); - - } - - public static String getCgToken(Context context){ - SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); - return sp.getString(CAREGIVER_TOKEN, null); - } - - public static void setWantSecurityFlag(Context context, int watSecurity){ - SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - - editor.putInt(CG_APP_SECURITY, watSecurity); - - editor.apply(); - } - - public static int getWantSecurityFlag(Context context){ - SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); - return sp.getInt(CG_APP_SECURITY, NOT_ASKED_CG_SECURITY); - } - - public static void cgSignOut(Context context){ - saveCgData(null, -1, context); - setWantSecurityFlag(context, NOT_ASKED_CG_SECURITY); - } - public static void dialPhone(Context activity, String phone_number) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", phone_number, null)); @@ -382,4 +313,180 @@ public abstract class AppUtil { binding.btn.setText(btn_text); binding.btn.setOnClickListener(btn_clickListener); } + + + // ********************* USER DATA UTILS ****************************************** + + public static void savePatientData(String token, int patient_uid, Context context, boolean isLoggedIn){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + + editor.putString(PATIENT_TOKEN, token); + editor.putInt(PATIENT_UID, patient_uid); + editor.putBoolean(IS_PATIENT_LOGGED_IN, isLoggedIn); + + editor.apply(); + + Log.d(TAG, "saveToken: user token saved successful"); + + } + + public static String getPatientToken(Context context){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return sp.getString(PATIENT_TOKEN, ""); + } + + public static int getPatientUid(Context context){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return sp.getInt(PATIENT_UID, -1); + } + + public static boolean isPatientLoggedIn(Context context) { + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return sp.getBoolean(IS_PATIENT_LOGGED_IN, false); + } + + public static void patientSignOut(Context context){ + AppUtil.savePatientData(null, -1, context, false); + + // app block list clear + MySharedPref mySharedPref = new MySharedPref(context); + Set appList = mySharedPref.getArrayList("APP_LIST"); + if (appList != null) { + appList.clear(); + mySharedPref.setArrayList("APP_LIST", appList); + } + + // geofence details clear + updatePatientGeofence(context, null, null, null, null); + // removing geofence + GeofencingClient geofencingClient = LocationServices.getGeofencingClient(context); + geofencingClient.removeGeofences(Collections.singletonList(GEOFENCE_ID)).addOnSuccessListener(v -> { + Log.d(GEOFENCE_TAG, "patientSignOut: GEOFENCE REMOVED"); + }).addOnFailureListener(v -> { + Log.d(GEOFENCE_TAG, "patientSignOut: GEOFENCE COULDN'T BE REMOVE " + v); + }); + + // closing notifications + OneSignal.getUser().getPushSubscription().optOut(); + } + + public static void saveCgData(String token, int patient_id, Context context){ + SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + + editor.putString(CAREGIVER_TOKEN, token); + + editor.apply(); + + // now saving patient data + // This is important as there are instances where patient data such as patient id and patient_token is required + savePatientData(token, patient_id, context, false); + + Log.d(TAG, "saveToken: caregiver token saved successful"); + + } + + public static String getCgToken(Context context){ + SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); + return sp.getString(CAREGIVER_TOKEN, null); + } + + public static void setWantSecurityFlag(Context context, int watSecurity){ + SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + + editor.putInt(CG_APP_SECURITY, watSecurity); + + editor.apply(); + } + + public static int getWantSecurityFlag(Context context){ + SharedPreferences sp = context.getSharedPreferences(CAREGIVER_DETAILS, Context.MODE_PRIVATE); + return sp.getInt(CG_APP_SECURITY, NOT_ASKED_CG_SECURITY); + } + + public static void cgSignOut(Context context){ + saveCgData(null, -1, context); + setWantSecurityFlag(context, NOT_ASKED_CG_SECURITY); + + // closing notification + OneSignal.getUser().getPushSubscription().optOut(); + } + + // patient geofencing + private static final String PATIENT_GEOFENCE_RADIUS = "patient_geofence_radius"; + private static final String PATIENT_GEOFENCE_RADIUS_UNIT = "patient_geofence_radius_unit"; + private static final String PATIENT_GEOFENCE_LATITUDE = "patient_geofence_latitude"; + private static final String PATIENT_GEOFENCE_LONGITUDE = "patient_geofence_longitude"; + + public static void updatePatientGeofence(Context context, String lat, String lng, String radius, String radius_unit){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + + editor.putString(PATIENT_GEOFENCE_LATITUDE, lat); + editor.putString(PATIENT_GEOFENCE_LONGITUDE, lng); + editor.putString(PATIENT_GEOFENCE_RADIUS, radius); + editor.putString(PATIENT_GEOFENCE_RADIUS_UNIT, radius_unit); + + editor.apply(); + Log.d(GEOFENCE_TAG, "updatePatientGeofence: UPDATED"); + } + + public static String[] getPatientLatLng(Context context){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return new String[]{sp.getString(PATIENT_GEOFENCE_LATITUDE, null), sp.getString(PATIENT_GEOFENCE_LONGITUDE, null)}; + } + + public static String getPatientGeofenceRadius(Context context){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return sp.getString(PATIENT_GEOFENCE_RADIUS, null); + } + + public static String getPatientGeofenceRadiusUnit(Context context){ + SharedPreferences sp = context.getSharedPreferences(PATIENT_DETAILS, Context.MODE_PRIVATE); + return sp.getString(PATIENT_GEOFENCE_RADIUS_UNIT, null); + } + + public static boolean shouldAddPatientGeofence(@NonNull Context context, + @NonNull String remote_radius, + @NonNull String remote_radius_unit, + @NonNull PatientData data) { + + String local_radius = getPatientGeofenceRadius(context); + + if (local_radius != null){ + try { + float local_radius_f = Float.parseFloat(local_radius); + float remote_radius_f = Float.parseFloat(remote_radius); + + if (local_radius_f != remote_radius_f){ + // radius' are different + // thus, should add geofence + return true; + } + }catch (Exception e){ + // do nothing + } + } + + // radius is equal thus, checking for unit of the radius + String local_radius_unit = getPatientGeofenceRadiusUnit(context); + if (!remote_radius_unit.equals(local_radius_unit)){ + // remote radius units are different + // thus, should add geofence + return true; + } + + // checking for lat lng + String[] local_lat_lng = getPatientLatLng(context); + if (data.lat != null && data.lng != null) { + if (!data.lat.equals(local_lat_lng[0]) || !data.lng.equals(local_lat_lng[1])){ + // remote lat lng has changed + return true; + } + } + + return false; + } } diff --git a/app/src/main/java/com/ssb/simplitend/apputils/NotificationService.java b/app/src/main/java/com/ssb/simplitend/apputils/NotificationService.java index 4acf48e..62bce52 100644 --- a/app/src/main/java/com/ssb/simplitend/apputils/NotificationService.java +++ b/app/src/main/java/com/ssb/simplitend/apputils/NotificationService.java @@ -1,11 +1,29 @@ package com.ssb.simplitend.apputils; +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_ID; +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_TAG; + +import android.Manifest; +import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.RequiresPermission; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingClient; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.maps.model.LatLng; import com.onesignal.notifications.INotificationReceivedEvent; import com.onesignal.notifications.INotificationServiceExtension; +import com.ssb.simplitend.patientgeofencing.GeoFenceHelper; import org.json.JSONException; import org.json.JSONObject; @@ -20,7 +38,7 @@ public class NotificationService implements INotificationServiceExtension { public void onNotificationReceived(@NonNull INotificationReceivedEvent iNotificationReceivedEvent) { JSONObject extras = iNotificationReceivedEvent.getNotification().getAdditionalData(); String content_type = null; - if (extras != null){ + if (extras != null) { try { content_type = extras.getString("content_type"); } catch (JSONException e) { @@ -34,5 +52,49 @@ public class NotificationService implements INotificationServiceExtension { intent.putExtra(NOTIFICATION_TITLE_KEY, iNotificationReceivedEvent.getNotification().getTitle()); iNotificationReceivedEvent.getContext().sendBroadcast(intent); + + if (Constants.GEOFENCING_RADIUS_UPDATED.equals(content_type) || Constants.HOME_LOCATION_UPDATED.equals(content_type)) { + if (AppUtil.isPatientLoggedIn(iNotificationReceivedEvent.getContext())){ + // Either Geofence radius or Patient's location has changed. + Log.d(GEOFENCE_TAG, "Adding geo fence..."); + if (ActivityCompat.checkSelfPermission(iNotificationReceivedEvent.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(GEOFENCE_TAG, "No location permission"); + return; + } + +// addGeoFence(new LatLng(18.933154827942843, 72.82790520714602), +// 200, iNotificationReceivedEvent.getContext()); + }else{ + Log.d(GEOFENCE_TAG, "onNotificationReceived of PATIENT GEOFENCE CHANGED. BUT PATIENT IS LOGGED OUT."); + } + } + } + + @RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION) + private void addGeoFence(@NonNull LatLng latLng, float GEOFENCING_RADIUS , Context context) { + GeoFenceHelper geoFenceHelper = new GeoFenceHelper(context); + GeofencingClient geofencingClient = LocationServices.getGeofencingClient(context); + + // checking for background location updates for API level 29 and above + if (Build.VERSION.SDK_INT >= 29) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(GEOFENCE_TAG, "addGeoFence: No background location permission"); + return; + } + } + + Geofence geofence = geoFenceHelper.getGeoFence(GEOFENCE_ID, latLng, GEOFENCING_RADIUS, + Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT); + GeofencingRequest geofencingRequest = geoFenceHelper.getGeoFencingRequest(geofence); + PendingIntent pendingIntent = geoFenceHelper.getPendingIntent(); + + geofencingClient.addGeofences(geofencingRequest, pendingIntent) + .addOnSuccessListener(aVoid -> { + Log.d(GEOFENCE_TAG, "Geofence added successfully."); + AppUtil.updatePatientGeofence(context, latLng.latitude+"", latLng.longitude+"", + GEOFENCING_RADIUS+"", "kms"); + }) + .addOnFailureListener(e -> Log.d(GEOFENCE_TAG, "onFailure: Geofence couldn't be added: " + e.getLocalizedMessage())); + } } diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/activities/CaregiverDashActivity.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/activities/CaregiverDashActivity.java index d0d0fda..0fe0409 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/activities/CaregiverDashActivity.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/activities/CaregiverDashActivity.java @@ -199,6 +199,8 @@ public class CaregiverDashActivity extends AppCompatActivity implements // initializing dashboard fragment replaceFragment(new CgDashBoardFragment(), "dashboard"); + OneSignal.getUser().getPushSubscription().optIn(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // requestPermission will show the native Android notification permission prompt. OneSignal.getNotifications().requestPermission(true, Continue.with(r -> { diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/CgDashBoardFragment.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/CgDashBoardFragment.java index 305130f..ab36518 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/CgDashBoardFragment.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/CgDashBoardFragment.java @@ -32,6 +32,7 @@ import com.google.android.gms.maps.model.MarkerOptions; import com.ssb.simplitend.R; import com.ssb.simplitend.apputils.AppUtil; import com.ssb.simplitend.apputils.CaregiverDataCache; +import com.ssb.simplitend.apputils.Constants; import com.ssb.simplitend.articles.ArticleContracts; import com.ssb.simplitend.articles.ArticleResult; import com.ssb.simplitend.articles.ArticleShowerActivity; @@ -94,14 +95,12 @@ public class CgDashBoardFragment extends Fragment implements @Override public void onReceive(Context context, Intent intent) { String content_type = intent.getStringExtra(CONTENT_TYPE_KEY); - - loadReminders(); - loadActivities(); -// if (Constants.ACTIVITY_TIME.equals(content_type)){ -// loadArticles(); -// }else if (Constants.MEDICINE_TIME.equals(content_type)){ -// loadReminders(); -// } + + if (Constants.ACTIVITY_TIME.equals(content_type)){ + loadActivities(); + }else if (Constants.MEDICINE_TIME.equals(content_type)){ + loadReminders(); + } } }; @@ -317,7 +316,8 @@ public class CgDashBoardFragment extends Fragment implements progressDialog.setCancelable(false); progressDialog.show(); - viewModel.getGeoFenceDetails(careGiverData.caregiver_xid, + viewModel.getGeoFenceDetails("", + careGiverData.caregiver_xid + "", "Bearer " + AppUtil.getCgToken(requireContext()), this); @@ -354,6 +354,7 @@ public class CgDashBoardFragment extends Fragment implements .position(latLng) .title(patient_name); + mGoogleMap.clear(); mGoogleMap.addMarker(markerOptions); mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));; } diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/MyPatientFragment.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/MyPatientFragment.java index 2ffe7cc..e37aeb0 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/MyPatientFragment.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/fragments/MyPatientFragment.java @@ -118,7 +118,8 @@ public class MyPatientFragment extends Fragment implements CgHomeContracts.GetGe progressDialog.setCancelable(false); progressDialog.show(); - viewModel.getGeoFenceDetails(careGiverData.caregiver_xid, + viewModel.getGeoFenceDetails("", + careGiverData.caregiver_xid + "", "Bearer " + AppUtil.getCgToken(requireContext()), this); diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CaregiverMainViewModel.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CaregiverMainViewModel.java index e3e49fa..0a98e07 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CaregiverMainViewModel.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CaregiverMainViewModel.java @@ -64,10 +64,11 @@ public class CaregiverMainViewModel extends ViewModel { cgHomeRepository.saveGeoFenceDetails(geoFenceDetails, token, geoFenceCallback); } - public void getGeoFenceDetails(int cg_xid, + public void getGeoFenceDetails(String p_id, + String cg_xid, @NonNull String token, @NonNull CgHomeContracts.GetGeoFenceCallback getGeoFenceCallback){ - cgHomeRepository.getGeoFenceDetails(cg_xid, token, getGeoFenceCallback); + cgHomeRepository.getGeoFenceDetails(p_id, cg_xid, token, getGeoFenceCallback); } public void updateCaregiverData(int cg_xid, diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgDashboardApiService.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgDashboardApiService.java index 8cf0e33..a45372d 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgDashboardApiService.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgDashboardApiService.java @@ -18,11 +18,13 @@ import retrofit2.http.POST; import retrofit2.http.Part; import retrofit2.http.PartMap; import retrofit2.http.Path; +import retrofit2.http.Query; public interface CgDashboardApiService { - @GET("api/get-caregiver-patient-location-link/{id}") - Call> getGeofenceDetails(@Path("id") int id, + @GET("api/get-caregiver-patient-location-link") + Call> getGeofenceDetails(@Query("patientId") String p_id, + @Query("caregiverId") String c_id, @Header("Authorization") String token); @POST("api/caregiver-patient-location-link") diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgHomeRepository.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgHomeRepository.java index b3ebb8d..86fc3cf 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgHomeRepository.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/CgHomeRepository.java @@ -28,44 +28,45 @@ public class CgHomeRepository { private final CgDashboardApiService dash_apiService; - private CgHomeRepository(){ + private CgHomeRepository() { dash_apiService = RetrofitHelper.getRetrofit().create(CgDashboardApiService.class); } - public static synchronized CgHomeRepository getHomeRepository(){ - if (cgHomeRepository == null){ + public static synchronized CgHomeRepository getHomeRepository() { + if (cgHomeRepository == null) { cgHomeRepository = new CgHomeRepository(); } return cgHomeRepository; } - public void getArticles(@NonNull ArticleContracts.GetArticleCallback getArticleCallback){ + public void getArticles(@NonNull ArticleContracts.GetArticleCallback getArticleCallback) { ArticlePresenter articlePresenter = ArticlePresenter.getArticlePresenter(); articlePresenter.getArticles(getArticleCallback); } - public void getGeoFenceDetails(int cg_xid, + public void getGeoFenceDetails(String p_id, + String cg_xid, @NonNull String token, - @NonNull CgHomeContracts.GetGeoFenceCallback getGeoFenceCallback){ + @NonNull CgHomeContracts.GetGeoFenceCallback getGeoFenceCallback) { - dash_apiService.getGeofenceDetails(cg_xid, token) + dash_apiService.getGeofenceDetails(p_id, cg_xid, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200){ + if (response.body() != null) { + if (response.body().status != 200) { getGeoFenceCallback.onGeofenceDetailsFetchFailed(new Exception(), response.body().message); return; } - if (response.body().result == null){ + if (response.body().result == null) { getGeoFenceCallback.onGeofenceDetailsFetched(new GeoFenceDetails()); - }else{ + } else { getGeoFenceCallback.onGeofenceDetailsFetched(response.body().result); } - }else{ + } else { getGeoFenceCallback.onGeofenceDetailsFetchFailed(new Exception(), "Couldn't load GeoFence."); } } @@ -80,20 +81,20 @@ public class CgHomeRepository { public void saveGeoFenceDetails(@NonNull GeoFenceDetails geoFenceDetails, @NonNull String token, - @NonNull CgHomeContracts.SaveGeoFenceCallback geoFenceCallback){ + @NonNull CgHomeContracts.SaveGeoFenceCallback geoFenceCallback) { dash_apiService.saveGeoFenceDetails(geoFenceDetails, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200 || response.body().result == null){ + if (response.body() != null) { + if (response.body().status != 200 || response.body().result == null) { geoFenceCallback.onGeofenceDetailsSaveFailed(new Exception(), response.body().message); return; } geoFenceCallback.onGeofenceDetailsSaved(response.body().result); - }else{ + } else { geoFenceCallback.onGeofenceDetailsSaveFailed(new Exception(), "Please try again later."); } } @@ -110,19 +111,19 @@ public class CgHomeRepository { Map body, MultipartBody.Part photo, @NonNull String token, - @NonNull CgHomeContracts.UpdateCaregiverDataCallback updateCaregiverDataCallback){ + @NonNull CgHomeContracts.UpdateCaregiverDataCallback updateCaregiverDataCallback) { dash_apiService.updateCaregiverData(cg_xid, body, photo, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200 || response.body().result == null){ + if (response.body() != null) { + if (response.body().status != 200 || response.body().result == null) { updateCaregiverDataCallback.onCaregiverDateUpdateFailed(new Exception(), response.body().message); return; } updateCaregiverDataCallback.onCaregiverDataUpdated(response.body().result); - }else{ + } else { updateCaregiverDataCallback.onCaregiverDateUpdateFailed(new Exception(), "Please try again later."); } } @@ -138,19 +139,19 @@ public class CgHomeRepository { Map body, MultipartBody.Part photo, String token, - @NonNull CgHomeContracts.UpdatePatientDataCallback patientDataCallback){ + @NonNull CgHomeContracts.UpdatePatientDataCallback patientDataCallback) { dash_apiService.updatePatientData(patient_xid, body, photo, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200 || response.body().result == null){ + if (response.body() != null) { + if (response.body().status != 200 || response.body().result == null) { patientDataCallback.onPatientDataUpdateFailed(new Exception(), response.body().message); return; } patientDataCallback.onPatientDataUpdated(response.body().result); - }else{ + } else { patientDataCallback.onPatientDataUpdateFailed(new Exception(), "Please try again later."); } } @@ -165,19 +166,19 @@ public class CgHomeRepository { public void updatePatientAddress(int pat_id, Map body, @NonNull String token, - @NonNull CgHomeContracts.UpdatePatientAddressCallback patientAddressCallback){ + @NonNull CgHomeContracts.UpdatePatientAddressCallback patientAddressCallback) { dash_apiService.updatePatientAddress(pat_id, body, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200 || response.body().result == null){ + if (response.body() != null) { + if (response.body().status != 200 || response.body().result == null) { patientAddressCallback.onPatientAddressUpdateFailed(new Exception(), response.body().message); return; } patientAddressCallback.onPatientAddressUpdated(response.body().result); - }else{ + } else { patientAddressCallback.onPatientAddressUpdateFailed(new Exception(), "Please try again later."); } } @@ -190,22 +191,22 @@ public class CgHomeRepository { } public void updateCgPassword(int cg_xid, - Map body, - @NonNull String token, - @NonNull CgHomeContracts.UpdateCgPasswordCallback passwordCallback){ + Map body, + @NonNull String token, + @NonNull CgHomeContracts.UpdateCgPasswordCallback passwordCallback) { dash_apiService.updateCgPassword(cg_xid, body, token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200 || response.body().result == null){ + if (response.body() != null) { + if (response.body().status != 200 || response.body().result == null) { passwordCallback.onPasswordUpdateFailed(new Exception(), response.body().message); return; } passwordCallback.onPasswordUpdated(response.body().result); - }else{ + } else { passwordCallback.onPasswordUpdateFailed(new Exception(), "Please try again later."); } } @@ -219,19 +220,19 @@ public class CgHomeRepository { } public void signOut(String token, - @NonNull CgHomeContracts.SignOutCallback signOutCallback){ + @NonNull CgHomeContracts.SignOutCallback signOutCallback) { dash_apiService.signOut(token) .enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { - if (response.body() != null){ - if (response.body().status != 200){ + if (response.body() != null) { + if (response.body().status != 200) { signOutCallback.onSignOutFailed(new Exception(), response.body().message); return; } signOutCallback.onSignOutSuccess(); - }else{ + } else { signOutCallback.onSignOutFailed(new Exception(), "Please try again later."); } } diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/NotificationApiService.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/NotificationApiService.java new file mode 100644 index 0000000..6cdd978 --- /dev/null +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/NotificationApiService.java @@ -0,0 +1,18 @@ +package com.ssb.simplitend.caregiverdashboard.mvvm; + +import com.ssb.simplitend.welcome.welcomepatient.mvvm.models.CallResponse; + +import java.util.Map; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.Header; +import retrofit2.http.POST; + +public interface NotificationApiService { + + @POST("api/send-out-of-geofence-notification") + Call> notifyOutOfGeoFence(@Body Map body, + @Header("Authorization") String token); + +} diff --git a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/models/GeoFenceDetails.java b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/models/GeoFenceDetails.java index 887d978..a3cbb83 100644 --- a/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/models/GeoFenceDetails.java +++ b/app/src/main/java/com/ssb/simplitend/caregiverdashboard/mvvm/models/GeoFenceDetails.java @@ -14,4 +14,19 @@ public class GeoFenceDetails implements Serializable { public String updated_at; public GeoFenceDetails(){} + + @Override + public String toString() { + return "GeoFenceDetails{" + + "id=" + id + + ", caregiver_xid='" + caregiver_xid + '\'' + + ", patient_xid='" + patient_xid + '\'' + + ", location_name='" + location_name + '\'' + + ", radius='" + radius + '\'' + + ", type='" + type + '\'' + + ", message='" + message + '\'' + + ", created_at='" + created_at + '\'' + + ", updated_at='" + updated_at + '\'' + + '}'; + } } \ No newline at end of file diff --git a/app/src/main/java/com/ssb/simplitend/cg_geofencing/CgGeoFencingActivity.java b/app/src/main/java/com/ssb/simplitend/cg_geofencing/CgGeoFencingActivity.java index 7c8c69a..b184025 100644 --- a/app/src/main/java/com/ssb/simplitend/cg_geofencing/CgGeoFencingActivity.java +++ b/app/src/main/java/com/ssb/simplitend/cg_geofencing/CgGeoFencingActivity.java @@ -450,6 +450,7 @@ public class CgGeoFencingActivity extends AppCompatActivity implements OnMapRead public void onPatientAddressUpdated(@NonNull PatientData patientData) { binding.updateBtnsView.setVisibility(View.GONE); this.careGiverData.patientDetails = patientData; + this.patientData = patientData; CaregiverDataCache.setCareGiverData(this.careGiverData); progressDialog.dismiss(); Toast.makeText(this, "Patient's location updated.", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/ssb/simplitend/patient_dashboard/DashBoardActivity.java b/app/src/main/java/com/ssb/simplitend/patient_dashboard/DashBoardActivity.java index fe53fc0..58125c9 100644 --- a/app/src/main/java/com/ssb/simplitend/patient_dashboard/DashBoardActivity.java +++ b/app/src/main/java/com/ssb/simplitend/patient_dashboard/DashBoardActivity.java @@ -1,33 +1,104 @@ package com.ssb.simplitend.patient_dashboard; +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_TAG; + +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; +import android.util.Log; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.ViewModelProvider; import com.onesignal.Continue; import com.onesignal.OneSignal; import com.ssb.simplitend.R; +import com.ssb.simplitend.apputils.AppUtil; +import com.ssb.simplitend.apputils.PatientDataCache; +import com.ssb.simplitend.caregiverdashboard.mvvm.CgHomeContracts; +import com.ssb.simplitend.caregiverdashboard.mvvm.models.GeoFenceDetails; + +public class DashBoardActivity extends AppCompatActivity implements CgHomeContracts.GetGeoFenceCallback { + + protected PatientMainViewModel viewModel; + public static final int LOCATION_REQUEST_CODE = 1001; + + private GeoFenceDetails geoFenceDetails; + + private boolean geofencing_verified = false; -public class DashBoardActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dash_board_cp); + viewModel = new ViewModelProvider(this).get(PatientMainViewModel.class); + // requestPermission will show the native Android notification permission prompt. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ // requestPermission will show the native Android notification permission prompt. + geofencing_verified = false; OneSignal.getNotifications().requestPermission(true, Continue.with(r -> { + geofencing_verified = true; if (r.isSuccess()) { - if (r.getData() != null) { - // `requestPermission` completed successfully and the user has accepted permission - } - else { - // `requestPermission` completed successfully but the user has rejected permission - } + viewModel.getGeoFenceDetails("" + AppUtil.getPatientUid(this), + "", "Bearer " + AppUtil.getPatientToken(this), + this); } })); + + if (!geofencing_verified){ + viewModel.getGeoFenceDetails("" + AppUtil.getPatientUid(this), + "", "Bearer " + AppUtil.getPatientToken(this), + this); + } + + }else{ + viewModel.getGeoFenceDetails("" + AppUtil.getPatientUid(this), + "", "Bearer " + AppUtil.getPatientToken(this), + this); + } + + } + + private void validateAndAddGeofence(GeoFenceDetails geoFenceDetails) { + if (geoFenceDetails.radius != null && geoFenceDetails.type != null){ + PatientDataCache.getPatientData(this, (patientData -> { + if (patientData != null){ + if (AppUtil.shouldAddPatientGeofence(this, + geoFenceDetails.radius, geoFenceDetails.type, patientData)){ + // should add a geofence + viewModel.setGeofence(this, geoFenceDetails, patientData); + }else{ + Log.d(GEOFENCE_TAG, "onGeofenceDetailsFetched: should not add patient geofence because GEOFENCE DETAILS: " + geoFenceDetails + " PATIENT DETAILS: " + patientData); + } + } + }), false); + } + } + + @Override + public void onGeofenceDetailsFetched(@NonNull GeoFenceDetails geoFenceDetails) { + this.geoFenceDetails = geoFenceDetails; + validateAndAddGeofence(geoFenceDetails); + } + + @Override + public void onGeofenceDetailsFetchFailed(Throwable throwable, String message) { + Log.d(GEOFENCE_TAG, "onGeofenceDetailsFetchFailed: " + message); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == LOCATION_REQUEST_CODE){ + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ + Log.d(GEOFENCE_TAG, "onRequestPermissionsResult: PERMISSION GRANTED"); + if (geoFenceDetails != null){ + validateAndAddGeofence(geoFenceDetails); + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/ssb/simplitend/patient_dashboard/PatientMainViewModel.java b/app/src/main/java/com/ssb/simplitend/patient_dashboard/PatientMainViewModel.java new file mode 100644 index 0000000..910d052 --- /dev/null +++ b/app/src/main/java/com/ssb/simplitend/patient_dashboard/PatientMainViewModel.java @@ -0,0 +1,107 @@ +package com.ssb.simplitend.patient_dashboard; + +import static com.ssb.simplitend.patient_dashboard.DashBoardActivity.LOCATION_REQUEST_CODE; +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_ID; +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_TAG; + +import android.Manifest; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresPermission; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.ViewModel; + +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingClient; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.maps.model.LatLng; +import com.ssb.simplitend.apputils.AppUtil; +import com.ssb.simplitend.caregiverdashboard.mvvm.CgHomeContracts; +import com.ssb.simplitend.caregiverdashboard.mvvm.CgHomeRepository; +import com.ssb.simplitend.caregiverdashboard.mvvm.models.GeoFenceDetails; +import com.ssb.simplitend.patientgeofencing.GeoFenceHelper; +import com.ssb.simplitend.welcome.welcomepatient.mvvm.models.PatientData; + +public class PatientMainViewModel extends ViewModel { + + private final CgHomeRepository cgHomeRepository; + + public PatientMainViewModel() { + cgHomeRepository = CgHomeRepository.getHomeRepository(); + } + + public void setGeofence(Activity activity, GeoFenceDetails geoFenceDetails, PatientData patientData) { + LatLng latLng; + float radius; + try { + latLng = new LatLng(Double.parseDouble(patientData.lat), Double.parseDouble(patientData.lng)); + radius = Float.parseFloat(geoFenceDetails.radius); + } catch (Exception e) { + latLng = null; + radius = 0; + } + + if (latLng != null && radius != 0) { + if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(GEOFENCE_TAG, "setGeofence: REQUESTING FINE LOCATION PERMISSION" ); + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); + return; + } + + addGeoFence(latLng, radius, activity, geoFenceDetails.type); + } + } + + public void getGeoFenceDetails(String p_id, + String cg_xid, + @NonNull String token, + @NonNull CgHomeContracts.GetGeoFenceCallback getGeoFenceCallback){ + cgHomeRepository.getGeoFenceDetails(p_id, cg_xid, token, getGeoFenceCallback); + } + + @RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION) + private void addGeoFence(@NonNull LatLng latLng, float GEOFENCING_RADIUS , Activity activity, String unit) { + GeoFenceHelper geoFenceHelper = new GeoFenceHelper(activity); + GeofencingClient geofencingClient = LocationServices.getGeofencingClient(activity); + + // checking for background location updates for API level 29 and above + if (Build.VERSION.SDK_INT >= 29) { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(GEOFENCE_TAG, "setGeofence: REQUESTING BACKGROUND LOCATION PERMISSION" ); + AppUtil.showSOSDecision(activity, + "We understand your privacy.\nTo enable Geofencing we need to access your location while the app is closed.\nKindly click on the Settings button below and select \"Allow all the time\"", + "Settings", "No thanks", view -> { + // No thanks click + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, LOCATION_REQUEST_CODE); + }, view -> { + // Settings click + Toast.makeText(activity, "Geofencing is off.", Toast.LENGTH_SHORT).show(); + Log.d(GEOFENCE_TAG, "setGeofence: User chose not to allow background location."); + }); + return; + } + } + + Geofence geofence = geoFenceHelper.getGeoFence(GEOFENCE_ID, latLng, GEOFENCING_RADIUS, + Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT); + GeofencingRequest geofencingRequest = geoFenceHelper.getGeoFencingRequest(geofence); + PendingIntent pendingIntent = geoFenceHelper.getPendingIntent(); + + geofencingClient.addGeofences(geofencingRequest, pendingIntent) + .addOnSuccessListener(aVoid -> { + Log.d(GEOFENCE_TAG, "Geofence added successfully."); + AppUtil.updatePatientGeofence(activity, latLng.latitude+"", latLng.longitude+"", + GEOFENCING_RADIUS+"", unit); + }) + .addOnFailureListener(e -> Log.d(GEOFENCE_TAG, "onFailure: Geofence couldn't be added: " + e.getLocalizedMessage())); + + } +} diff --git a/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceBroadcastReceiver.java b/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceBroadcastReceiver.java new file mode 100644 index 0000000..7288028 --- /dev/null +++ b/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceBroadcastReceiver.java @@ -0,0 +1,77 @@ +package com.ssb.simplitend.patientgeofencing; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingEvent; +import com.ssb.simplitend.apputils.AppUtil; +import com.ssb.simplitend.apputils.RetrofitHelper; +import com.ssb.simplitend.caregiverdashboard.mvvm.NotificationApiService; +import com.ssb.simplitend.welcome.welcomepatient.mvvm.models.CallResponse; + +import static com.ssb.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_TAG; + +import java.util.HashMap; +import java.util.Map; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class GeoFenceBroadcastReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); + + if (geofencingEvent == null || geofencingEvent.hasError()) { + Log.d(GEOFENCE_TAG, "onReceive: Couldn't add geofence"); + return; + } + + int transition_type = geofencingEvent.getGeofenceTransition(); + + switch (transition_type) { + case Geofence.GEOFENCE_TRANSITION_DWELL: + Log.d(GEOFENCE_TAG, "onReceive: DWELL"); + break; + case Geofence.GEOFENCE_TRANSITION_ENTER: + Log.d(GEOFENCE_TAG, "onReceive: ENTER"); + break; + case Geofence.GEOFENCE_TRANSITION_EXIT: + Log.d(GEOFENCE_TAG, "onReceive: EXIT"); + notifyOutOfGeofence(context); + break; + } + } + + private void notifyOutOfGeofence(Context context) { + Log.d(GEOFENCE_TAG, "Sending notification to patient"); + NotificationApiService apiService = RetrofitHelper.getRetrofit().create(NotificationApiService.class); + + Map body = new HashMap<>(); + body.put("patient_id", AppUtil.getPatientUid(context) + ""); + + apiService.notifyOutOfGeoFence(body, "Bearer " + AppUtil.getPatientToken(context)) + .enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.code() == 200){ + Log.d(GEOFENCE_TAG, "OUT OF GEOFENCE NOTIFICATION SENT SUCCESSFULLY."); + }else{ + Log.d(GEOFENCE_TAG, "Couldn't notify patient " + response.message()); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + Log.d(GEOFENCE_TAG, "Couldn't notify patient due to " + t); + } + }); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceHelper.java b/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceHelper.java new file mode 100644 index 0000000..9eb819f --- /dev/null +++ b/app/src/main/java/com/ssb/simplitend/patientgeofencing/GeoFenceHelper.java @@ -0,0 +1,63 @@ +package com.ssb.simplitend.patientgeofencing; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.os.Build; + +import androidx.annotation.NonNull; + +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.maps.model.LatLng; + +public class GeoFenceHelper extends ContextWrapper { + + public static final String GEOFENCE_TAG = "PATIENT_GEOFENCE"; + private PendingIntent pendingIntent; + + public static final int BROADCAST_REQUEST_CODE = 10001; + public static final String GEOFENCE_ACTION = "com.simplitend.ACTION_GEOFENCE"; + + public static final String GEOFENCE_ID = "com.simplitent.PATIENT_GEOFENCE"; + + public GeoFenceHelper(Context base) { + super(base); + } + + public GeofencingRequest getGeoFencingRequest(Geofence geofence){ + return new GeofencingRequest.Builder() + .addGeofence(geofence) + .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_EXIT) + .build(); + } + + public Geofence getGeoFence(@NonNull String GEOFENCE_ID, + @NonNull LatLng latLng, float radius, int transitionType){ + return new Geofence.Builder() + .setCircularRegion(latLng.latitude, latLng.longitude, radius) + .setRequestId(GEOFENCE_ID) + .setTransitionTypes(transitionType) + .setExpirationDuration(Geofence.NEVER_EXPIRE) + .setLoiteringDelay(5000) // This does nothing if the transition is not DWELL + .build(); + } + + public synchronized PendingIntent getPendingIntent(){ + if (pendingIntent != null){ + return pendingIntent; + } + + Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class); + intent.setAction(GEOFENCE_ACTION); + + if(Build.VERSION.SDK_INT > 30){ + pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + } else{ + pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + return pendingIntent; + } +}