liufuhua007

2 version add patient view

Showing 28 changed files with 994 additions and 21 deletions
1 +package com.sw.laryngoscope.FHIR.adapter;
2 +
3 +import android.view.ViewGroup;
4 +
5 +import androidx.annotation.NonNull;
6 +import androidx.recyclerview.widget.RecyclerView;
7 +
8 +import com.sw.laryngoscope.FHIR.model.PatientModel;
9 +
10 +import java.util.ArrayList;
11 +import java.util.HashMap;
12 +
13 +public abstract class BaseAdapter<H extends BaseViewHolder> extends RecyclerView.Adapter<BaseViewHolder> {
14 + private HashMap<String, PatientModel> patientsHashMap;
15 + private ArrayList<PatientModel> uniquePatients = new ArrayList<>();
16 +
17 + /**
18 + * Constructor.
19 + */
20 + BaseAdapter(HashMap<String, PatientModel> patientsHashMap) {
21 + this.patientsHashMap = patientsHashMap;
22 + }
23 +
24 + /**
25 + * Getter for PatientHashMap
26 + * @return the patientHashMap
27 + */
28 + HashMap<String, PatientModel> getPatientsHashMap() {
29 + return patientsHashMap;
30 + }
31 +
32 + /**
33 + * Setter for PatientHashMap
34 + * @param patientsHashMap new PatientHashMap
35 + */
36 + void setPatientsHashMap(HashMap<String, PatientModel> patientsHashMap) {
37 + this.patientsHashMap = patientsHashMap;
38 + }
39 +
40 +
41 + /**
42 + * Getter for uniquePatients
43 + * @return uniquePatients
44 + */
45 + ArrayList<PatientModel> getUniquePatients() {
46 + return uniquePatients;
47 + }
48 +
49 + /**
50 + * Setter for uniquePatients
51 + * @param uniquePatients new uniquePatients list
52 + */
53 + void setUniquePatients(ArrayList<PatientModel> uniquePatients) {
54 + this.uniquePatients = uniquePatients;
55 + }
56 +
57 + /**
58 + * onCreateViewHolder overrides it superclass's ( RecyclerView ) method
59 + * subclass of this class must implement this method.
60 + */
61 + @NonNull
62 + @Override
63 + public abstract BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
64 +
65 + /**
66 + * onBindViewHolder overrides it superclass's ( RecyclerView ) method
67 + * subclass of this class must implement this method.
68 + */
69 + @Override
70 + public abstract void onBindViewHolder(@NonNull BaseViewHolder holder, int position);
71 +
72 + /**
73 + * onGetItemCount overrides it superclass's ( RecyclerView ) method
74 + * subclass of this class must implement this method.
75 + */
76 + @Override
77 + public abstract int getItemCount();
78 +}
1 +package com.sw.laryngoscope.FHIR.adapter;
2 +
3 +import android.view.View;
4 +
5 +import androidx.annotation.NonNull;
6 +import androidx.recyclerview.widget.RecyclerView;
7 +
8 +public abstract class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
9 + /**
10 + * Constructor.
11 + * @param itemView
12 + */
13 + BaseViewHolder(@NonNull View itemView) {
14 + super(itemView);
15 + }
16 +
17 + /**
18 + * On click method from View.OnClickListener interface
19 + * Subclass that inherits from this class must implement this method.
20 + */
21 + @Override
22 + public abstract void onClick(View v);
23 +
24 +}
1 +package com.sw.laryngoscope.FHIR.adapter;
2 +
3 +import android.view.LayoutInflater;
4 +import android.view.View;
5 +import android.view.ViewGroup;
6 +import android.widget.CheckBox;
7 +import android.widget.TextView;
8 +
9 +import androidx.annotation.NonNull;
10 +import androidx.constraintlayout.widget.ConstraintLayout;
11 +
12 +import com.google.android.material.chip.Chip;
13 +import com.sw.laryngoscope.R;
14 +import com.sw.laryngoscope.FHIR.model.PatientModel;
15 +import com.sw.laryngoscope.FHIR.model.observation.ObservationType;
16 +
17 +import java.util.ArrayList;
18 +import java.util.HashMap;
19 +import java.util.Objects;
20 +
21 +public class SelectPatientsAdapter extends BaseAdapter<SelectPatientsAdapter.SelectPatientViewHolder>{
22 + // select patients adapter on pcl
23 + private OnPatientClickListener onPatientClickListener;
24 + private ArrayList<PatientModel> selectedPatients;
25 + // A list to remember a patient's state
26 + // ( true when patient is selected , otherwise false)
27 + private ArrayList<Boolean> patientState = new ArrayList<>();
28 + /**
29 + * The SelectPatient Adapter constructor, this initialises the adapter that
30 + * will be used to update the SelectPatient fragment UI.
31 + *
32 + * @param patientsHashMap list of patients that is under HealthPractitioner
33 + * @param onPatientClickListener the listener that is listening to each patient's card clicks
34 + * @param selectedPatients a list of selected patients
35 + */
36 + public SelectPatientsAdapter(HashMap<String, PatientModel> patientsHashMap, OnPatientClickListener onPatientClickListener, ArrayList<PatientModel> selectedPatients) {
37 + super(patientsHashMap);
38 + this.selectedPatients = selectedPatients;
39 + this.onPatientClickListener = onPatientClickListener;
40 +
41 + ArrayList<PatientModel> uniquePatients = new ArrayList<>();
42 +
43 + // loop through the patient hash map to get a list of unique patients
44 + patientsHashMap.forEach((patientID, patientModel) -> {
45 + if (patientModel != null) {
46 +
47 + PatientModel selectedPatient = isSelectedPatient(patientModel);
48 + if (selectedPatient != null) {
49 + uniquePatients.add(selectedPatient);
50 + patientState.add(true);
51 + }
52 + else {
53 + uniquePatients.add(patientModel);
54 + patientState.add(false);
55 + }
56 + }
57 + });
58 + setUniquePatients(uniquePatients);
59 + }
60 +
61 + /**
62 + * This method checks if patient is already in the selected patient list.
63 + * @param patient - the patient model object
64 + * @return true - if patient is already in the selected patient list
65 + * false - if patient is not in the selected patient list
66 + */
67 + private PatientModel isSelectedPatient(PatientModel patient) {
68 + for (PatientModel p: selectedPatients) {
69 + if(p.getPatientID().equals(patient.getPatientID())) {
70 + return p;
71 + }
72 + }
73 + return null;
74 + }
75 +
76 + /**
77 + * This method overrides its superclass's onCreateViewHolder method
78 + * It is responsible for creating new card view holders that will be used to hold
79 + * and display patient'name
80 +
81 + * @return a ViewHolder Object
82 + */
83 + @NonNull
84 + @Override
85 + public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
86 + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.select_patients_cardview, parent, false);
87 + return new SelectPatientsAdapter.SelectPatientViewHolder(v, onPatientClickListener);
88 + }
89 +
90 + /**
91 + * This method overrides its superclass's onBindViewHolder method
92 + * This method is used to update the contents of the SelectPatientViewHolder to reflect the patient
93 + * at the given position.
94 + *
95 + * This method also checks if patient is a selected patient and do the following:
96 + * 1. if true -> highlight the patient's card view in green
97 + * 2. else -> don't highlight the patient's card view
98 + *
99 + * @param baseHolder the reference to the holder that will be used to display the patient's name
100 + * @param position the position of the adapter
101 + */
102 + @Override
103 + public void onBindViewHolder(@NonNull BaseViewHolder baseHolder, int position) {
104 + SelectPatientViewHolder holder = (SelectPatientViewHolder) baseHolder;
105 + holder.checkBox.setChecked(patientState.get(position));
106 + holder.background.setBackgroundResource(R.drawable.cardv_nonselected_bg);
107 + holder.patientName.setText(getUniquePatients().get(position).getName());
108 + PatientModel patient = getUniquePatients().get(position);
109 + if (isSelectedPatient(patient) != null){
110 + holder.checkBox.setChecked(true);
111 + holder.background.setBackgroundResource(R.drawable.cardv_selected_bg);
112 + patientState.set(position,true);
113 + onPatientClickListener.onPatientClick(holder.checkBox.isChecked(), getUniquePatients().get(position));
114 + // set chip to be checked if selected for monitoring observation(s) previously
115 + for (ObservationType type: ObservationType.values()) {
116 + switch (type) {
117 + case CHOLESTEROL:
118 + if (Objects.requireNonNull(isSelectedPatient(patient)).isObservationMonitored(type)) {
119 + holder.cholesterolChip.setChecked(true);
120 + }
121 + else {
122 + holder.cholesterolChip.setChecked(false);
123 + }
124 + case BLOOD_PRESSURE:
125 + if (Objects.requireNonNull(isSelectedPatient(patient)).isObservationMonitored(type)) {
126 + holder.bloodPressureChip.setChecked(true);
127 + }
128 + else {
129 + holder.bloodPressureChip.setChecked(false);
130 + }
131 + }
132 + }
133 + }
134 + }
135 +
136 + /**
137 + * This method overrides its superclass's method,
138 + * It gets the total number of patients that is under HeathPractitioner
139 + * @return total number of patients
140 + */
141 + @Override
142 + public int getItemCount() {
143 + return getUniquePatients().size();
144 + }
145 +
146 + /**
147 + * The SelectPatientViewHolder class are objects that holds the reference to the individual
148 + * card views that is reused to display different patient's data in the recycler view.
149 + *
150 + * This class implements View.OnClickListener interface.
151 + */
152 + public class SelectPatientViewHolder extends BaseViewHolder {
153 + private TextView patientName;
154 + private CheckBox checkBox; //hidden checkbox
155 + private SelectPatientsAdapter.OnPatientClickListener onPatientClickListener;
156 + private ConstraintLayout background;
157 + private Chip cholesterolChip;
158 + private Chip bloodPressureChip;
159 +
160 + /**
161 + * The SelectPatientViewHolder constructor
162 + * initialise SelectPatientViewHolder object that will be used to hold patient's data
163 + * @param onPatientClickListener the listener that is listening to patient's card clicks
164 + */
165 + SelectPatientViewHolder(@NonNull View itemView, OnPatientClickListener onPatientClickListener) {
166 + super(itemView);
167 + patientName = itemView.findViewById(R.id.all_patient_txt_name);
168 + checkBox = itemView.findViewById(R.id.checkBox);
169 + background = itemView.findViewById(R.id.card_backgroud);
170 + cholesterolChip = itemView.findViewById(R.id.cholesterol_chip);
171 + bloodPressureChip = itemView.findViewById(R.id.blood_pressure_chip);
172 + this.onPatientClickListener = onPatientClickListener;
173 + itemView.setOnClickListener(this);
174 + cholesterolChip.setOnClickListener(this);
175 + bloodPressureChip.setOnClickListener(this);
176 + }
177 +
178 + private void observationStatus(Chip chip, int position, ObservationType observationType) {
179 + if (chip.isChecked()) {
180 + getUniquePatients().get(position).monitorObservation(observationType, true);
181 + background.setBackgroundResource(R.drawable.cardv_selected_bg);
182 + checkBox.setChecked(true);
183 + patientState.set(position,true);
184 + }
185 + else {
186 + if (!cholesterolChip.isChecked() && !bloodPressureChip.isChecked()) {
187 + patientState.set(position,false);
188 + background.setBackgroundResource(R.drawable.cardv_nonselected_bg);
189 + checkBox.setChecked(false);
190 + }
191 + getUniquePatients().get(position).monitorObservation(observationType, false);
192 + }
193 + }
194 +
195 + /**
196 + * This method is called when user clicks on a patient's card-view.
197 + * It notifies its onClickListener (SelectPatientFragment) to add the patient into the
198 + * selected patient list/ remove the patient form the selected patient list
199 + */
200 + @Override
201 + public void onClick(View v) {
202 + switch (v.getId()) {
203 + case R.id.cholesterol_chip:
204 + observationStatus(cholesterolChip, getAdapterPosition(), ObservationType.CHOLESTEROL);
205 + break;
206 + case R.id.blood_pressure_chip:
207 + observationStatus(bloodPressureChip, getAdapterPosition(), ObservationType.BLOOD_PRESSURE);
208 + break;
209 + }
210 + onPatientClickListener.onPatientClick(checkBox.isChecked(), getUniquePatients().get(getAdapterPosition()));
211 + }
212 + }
213 +
214 + /**
215 + * Class the uses this interface must implement their own onPatientClick method.
216 + * onPatientClick is handle the events that should happen when a patient's card-view is clicked.
217 + */
218 + public interface OnPatientClickListener {
219 + void onPatientClick(boolean checked, PatientModel patient);
220 + }
221 +
222 +}
1 +package com.sw.laryngoscope.FHIR.fragment;
2 +
3 +
4 +import android.content.Context;
5 +import android.os.Bundle;
6 +import android.view.LayoutInflater;
7 +import android.view.View;
8 +import android.view.ViewGroup;
9 +import android.widget.Toast;
10 +
11 +import androidx.annotation.NonNull;
12 +import androidx.annotation.Nullable;
13 +import androidx.appcompat.widget.Toolbar;
14 +import androidx.fragment.app.Fragment;
15 +import androidx.lifecycle.Observer;
16 +import androidx.lifecycle.ViewModelProvider;
17 +import androidx.recyclerview.widget.LinearLayoutManager;
18 +
19 +import com.sw.laryngoscope.R;
20 +import com.sw.laryngoscope.FHIR.adapter.SelectPatientsAdapter;
21 +import com.sw.laryngoscope.databinding.FgPatientFragmentBinding;
22 +import com.sw.laryngoscope.FHIR.model.PatientModel;
23 +import com.sw.laryngoscope.FHIR.viewModel.SharedViewModel;
24 +
25 +import java.util.ArrayList;
26 +import java.util.HashMap;
27 +
28 +public class SelectPatientsFragment extends Fragment implements SelectPatientsAdapter.OnPatientClickListener{
29 + private String practitionerID;
30 + private SelectPatientsFragment thisFrag; // a reference to this fragment
31 +// private Toolbar toolbar;
32 + private SharedViewModel sharedViewModel;
33 +// private RecyclerView recyclerView;
34 + private ArrayList<PatientModel> selectedPatients = new ArrayList<>();
35 +// private TextView title;
36 +// private TextView loadingTextView;
37 +// private ImageButton backButton;
38 +// private ProgressBar loadingSpinner;
39 +
40 + private FgPatientFragmentBinding binding;
41 +
42 + /**
43 + * Constructor
44 + * @param practitionerID the Health Practitioner's ID
45 + */
46 + public SelectPatientsFragment(String practitionerID) {
47 + this.practitionerID = practitionerID;
48 + }
49 +
50 + @Override
51 + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
52 + @Nullable Bundle savedInstanceState) {
53 +
54 + binding = FgPatientFragmentBinding.inflate(inflater, container, false);
55 +
56 + return binding.getRoot();
57 + }
58 +
59 + public void onViewCreatred(@NonNull View view,@Nullable Bundle savedInstanceState){
60 +
61 + sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
62 +
63 + // find all the graphical components
64 +// recyclerView = root.findViewById(R.id.select_patients_recycler_view);
65 +// backButton = root.findViewById(R.id.btn_back);
66 +// toolbar = root.findViewById(R.id.select_patients_toolbar);
67 +// title = toolbar.findViewById(R.id.toolbar_title);
68 +// loadingTextView = root.findViewById(R.id.select_patients_txt_loading);
69 +// loadingSpinner = root.findViewById(R.id.select_patients_progressBar);
70 +
71 + binding.selectPatientsTxtLoading.setVisibility(View.VISIBLE);
72 + binding.selectPatientsProgressBar.setVisibility(View.VISIBLE);
73 +
74 + sharedViewModel.setPractitionerID(practitionerID);
75 + setUpToolBar();
76 +
77 + //update UI when there's new patient under Health Practitioner
78 + sharedViewModel.getAllPatients().observe(getViewLifecycleOwner(),patientUpdatedObserver);
79 +
80 + thisFrag = this;
81 + }
82 +
83 + /**
84 + * This observer observes for changes in patient list
85 + * ( The list that contains all patients under Health Practitioner )
86 + * It displays a loading screen when the app is still fetching data from the server,
87 + * It immediately updates the UI to display the patients when the system completes
88 + * the data fetching process.
89 + */
90 + private Observer<HashMap<String,PatientModel>> patientUpdatedObserver = new Observer<HashMap<String,PatientModel >>() {
91 + @Override
92 + public void onChanged( HashMap < String, PatientModel > patientHashMap) {
93 + if (patientHashMap.size() == 0) {
94 + binding.selectPatientsTxtLoading.setText(R.string.zero_patients_message);
95 + }
96 + else{
97 + binding.selectPatientsProgressBar.setVisibility(View.GONE);
98 + binding.selectPatientsTxtLoading.setVisibility(View.GONE);
99 + binding.selectPatientsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
100 + binding.selectPatientsRecyclerView.setAdapter( new SelectPatientsAdapter(patientHashMap,thisFrag, selectedPatients));
101 + }
102 + }
103 + };
104 +
105 + private void setUpToolBar() {
106 + binding.selectPatientsToolbar.toolbarTitle.setText(R.string.title_patient_selection);
107 + binding.selectPatientsToolbar.getRoot().inflateMenu(R.menu.select_patients_menu);
108 + Toolbar.OnMenuItemClickListener menuItemClickListener = item -> {
109 + if (selectedPatients.size() > 0) {
110 + // save list into view model
111 + sharedViewModel.setSelectedPatients(selectedPatients);
112 +// MainActivity main = (MainActivity) getActivity();
113 +// Objects.requireNonNull(main).findFragment(MainActivity.home_fragment);
114 + } else {
115 + String message = "Please select AT LEAST 1 patient";
116 + Context context = getContext();
117 + Toast toast = Toast.makeText(context, message, Toast.LENGTH_LONG);
118 + toast.show();
119 + }
120 + return true;
121 + };
122 + binding.selectPatientsToolbar.getRoot().setOnMenuItemClickListener(menuItemClickListener);
123 +
124 + binding.selectPatientsToolbar.btnBack.setOnClickListener(v -> {
125 +// MainActivity main = (MainActivity) getActivity();
126 +// Objects.requireNonNull(main).findFragment(MainActivity.login_fragment);
127 + });
128 + }
129 +
130 + public void onPatientClick(boolean checked, PatientModel patient) {
131 + PatientModel patientWithSameID = null;
132 + boolean found = false;
133 + for (PatientModel p : selectedPatients){
134 + if(p.getPatientID().equals(patient.getPatientID())){
135 + found = true;
136 + patientWithSameID = p;
137 + break;
138 + }
139 + }
140 + if (checked) {
141 + if (!found) {
142 + selectedPatients.add(patient);
143 + }
144 + } else if (!checked) {
145 + if(found){
146 + selectedPatients.remove(patientWithSameID);
147 + }else{
148 + selectedPatients.remove(patient);
149 + }
150 + }
151 + updateToolbar();
152 + }
153 +
154 + /**
155 + * This method updates the tool bar to display the total number of patients that is currently
156 + * being selected
157 + */
158 + private void updateToolbar() {
159 + String text = selectedPatients.size() + " Patients Selected";
160 + // Log.d("Select_Patient_Fragment","current sellected patient : " + selected_patients.toString());
161 + binding.selectPatientsToolbar.toolbarTitle.setText(text);
162 + }
163 +
164 +}
1 -package com.sw.laryngoscope.model; 1 +package com.sw.laryngoscope.FHIR.model;
2 2
3 public class PatientAddressModel { 3 public class PatientAddressModel {
4 private String city; 4 private String city;
......
1 -package com.sw.laryngoscope.model; 1 +package com.sw.laryngoscope.FHIR.model;
2 2
3 import android.text.format.DateFormat; 3 import android.text.format.DateFormat;
4 4
5 -import com.sw.laryngoscope.model.observation.BloodPressureObservationModel; 5 +import com.sw.laryngoscope.FHIR.model.observation.BloodPressureObservationModel;
6 -import com.sw.laryngoscope.model.observation.ObservationModel; 6 +import com.sw.laryngoscope.FHIR.model.observation.ObservationModel;
7 -import com.sw.laryngoscope.model.observation.ObservationType; 7 +import com.sw.laryngoscope.FHIR.model.observation.ObservationType;
8 8
9 import org.hl7.fhir.r4.model.Enumerations; 9 import org.hl7.fhir.r4.model.Enumerations;
10 10
......
1 -package com.sw.laryngoscope.model.observation; 1 +package com.sw.laryngoscope.FHIR.model.observation;
2 2
3 import org.hl7.fhir.r4.model.Observation; 3 import org.hl7.fhir.r4.model.Observation;
4 4
......
1 -package com.sw.laryngoscope.model.observation; 1 +package com.sw.laryngoscope.FHIR.model.observation;
2 2
3 import org.hl7.fhir.r4.model.Observation; 3 import org.hl7.fhir.r4.model.Observation;
4 4
......
1 -package com.sw.laryngoscope.model.observation; 1 +package com.sw.laryngoscope.FHIR.model.observation;
2 2
3 import org.hl7.fhir.r4.model.Observation; 3 import org.hl7.fhir.r4.model.Observation;
4 4
......
1 -package com.sw.laryngoscope.model.observation; 1 +package com.sw.laryngoscope.FHIR.model.observation;
2 2
3 public enum ObservationType { 3 public enum ObservationType {
4 CHOLESTEROL, BLOOD_PRESSURE; 4 CHOLESTEROL, BLOOD_PRESSURE;
......
1 -package com.sw.laryngoscope.service; 1 +package com.sw.laryngoscope.FHIR.service;
2 2
3 import ca.uhn.fhir.context.FhirContext; 3 import ca.uhn.fhir.context.FhirContext;
4 import ca.uhn.fhir.rest.client.api.IGenericClient; 4 import ca.uhn.fhir.rest.client.api.IGenericClient;
......
1 -package com.sw.laryngoscope.service.repository; 1 +package com.sw.laryngoscope.FHIR.service.repository;
2 2
3 -import com.sw.laryngoscope.model.observation.BloodPressureObservationModel; 3 +import com.sw.laryngoscope.FHIR.model.observation.BloodPressureObservationModel;
4 -import com.sw.laryngoscope.model.observation.CholesterolObservationModel; 4 +import com.sw.laryngoscope.FHIR.model.observation.CholesterolObservationModel;
5 -import com.sw.laryngoscope.model.observation.ObservationModel; 5 +import com.sw.laryngoscope.FHIR.model.observation.ObservationModel;
6 -import com.sw.laryngoscope.model.observation.ObservationType; 6 +import com.sw.laryngoscope.FHIR.model.observation.ObservationType;
7 -import com.sw.laryngoscope.service.FhirService; 7 +import com.sw.laryngoscope.FHIR.service.FhirService;
8 8
9 import org.hl7.fhir.r4.model.Bundle; 9 import org.hl7.fhir.r4.model.Bundle;
10 import org.hl7.fhir.r4.model.Observation; 10 import org.hl7.fhir.r4.model.Observation;
......
1 -package com.sw.laryngoscope.service.repository; 1 +package com.sw.laryngoscope.FHIR.service.repository;
2 2
3 -import com.sw.laryngoscope.model.PatientAddressModel; 3 +import com.sw.laryngoscope.FHIR.model.PatientAddressModel;
4 -import com.sw.laryngoscope.model.PatientModel; 4 +import com.sw.laryngoscope.FHIR.model.PatientModel;
5 -import com.sw.laryngoscope.service.FhirService; 5 +import com.sw.laryngoscope.FHIR.service.FhirService;
6 6
7 import org.hl7.fhir.r4.model.Bundle; 7 import org.hl7.fhir.r4.model.Bundle;
8 import org.hl7.fhir.r4.model.Encounter; 8 import org.hl7.fhir.r4.model.Encounter;
......
1 +package com.sw.laryngoscope.FHIR.viewModel;
2 +
3 +public interface Poll {
4 + void polling();
5 +}
1 +package com.sw.laryngoscope.FHIR.viewModel;
2 +
3 +import android.util.Log;
4 +
5 +import android.os.Handler;
6 +import android.os.HandlerThread;
7 +
8 +
9 +import androidx.lifecycle.MutableLiveData;
10 +import androidx.lifecycle.ViewModel;
11 +import androidx.lifecycle.LiveData;
12 +
13 +import com.sw.laryngoscope.FHIR.model.PatientModel;
14 +import com.sw.laryngoscope.FHIR.model.observation.ObservationModel;
15 +import com.sw.laryngoscope.FHIR.model.observation.ObservationType;
16 +import com.sw.laryngoscope.FHIR.service.repository.ObservationRepositoryFactory;
17 +import com.sw.laryngoscope.FHIR.service.repository.PatientRepository;
18 +
19 +import java.util.ArrayList;
20 +import java.util.HashMap;
21 +import java.util.Objects;
22 +
23 +public class SharedViewModel extends ViewModel implements Poll {
24 + // for polling
25 + private MutableLiveData<HashMap<String, PatientModel>> patientObservations = new MutableLiveData<>();
26 + // to get all the patients under practitioner
27 + private MutableLiveData<HashMap<String, PatientModel>> allPatients = new MutableLiveData<>();
28 + private String practitionerID = "";
29 + private PatientRepository patientRepository;
30 + private ObservationRepositoryFactory observationRepositoryFactory;
31 + private MutableLiveData<String> selectedFrequency = new MutableLiveData<>() ;
32 + private MutableLiveData<ArrayList<PatientModel>> selectedPatients = new MutableLiveData<>();
33 +
34 + private void initShareViewModel() {
35 + patientRepository = new PatientRepository(practitionerID);
36 + observationRepositoryFactory = new ObservationRepositoryFactory();
37 + fetchAllPatients();
38 + setSelectedPatients(new ArrayList<>());
39 + selectedFrequency.setValue("10");
40 + }
41 +
42 + public void setPractitionerID(String practitionerID) {
43 + this.practitionerID = practitionerID;
44 + initShareViewModel();
45 + }
46 +
47 + public LiveData<String> getSelectedFrequency() {
48 + return selectedFrequency;
49 + }
50 +
51 + public void updateCurrentSelected(String currentSelected) {
52 + this.selectedFrequency.setValue(currentSelected);
53 + }
54 +
55 + public LiveData<HashMap<String, PatientModel>> getAllPatientObservations() {
56 + return patientObservations;
57 + }
58 +
59 + public void setSelectedPatients(ArrayList<PatientModel> selectedPatientsArray) {
60 + selectedPatients.setValue(selectedPatientsArray);
61 + }
62 +
63 + public LiveData<HashMap<String, PatientModel>> getAllPatients() {
64 + return allPatients;
65 + }
66 +
67 + private ObservationModel getObservation(String id, ObservationType observationType) {
68 + return observationRepositoryFactory.getObservationModel(id, observationType);
69 + }
70 +
71 + private void fetchAllPatients() {
72 + // run asynchronous tasks on background thread to prevent network on main exception
73 + HandlerThread backgroundThread = new HandlerThread("Background Thread");
74 + backgroundThread.start();
75 + Handler timer = new Handler(backgroundThread.getLooper());
76 +
77 + timer.post(() -> {
78 + HashMap < String, PatientModel > patientHashMap = new HashMap<>();
79 + // loop through all patients
80 + for (PatientModel patient : patientRepository.getAllPatients()) {
81 + try {
82 + // only show patients with cholesterol and bp values
83 + for (ObservationType type: ObservationType.values()) {
84 + patient.setObservation(type, getObservation(patient.getPatientID(), type));
85 + patientHashMap.put(patient.getPatientID(), patient);
86 + }
87 + patient.addLatestBPReadings(observationRepositoryFactory.getLatestBloodPressureReadings(patient.getPatientID(),5));
88 +
89 + }
90 + // patient does not have the observation type
91 + catch (Exception e) {
92 + Log.e("Patient ", "No observation type");
93 + }
94 + }
95 +
96 + // update LiveData and notify observers - used by select patient
97 + allPatients.postValue(patientHashMap);
98 + });
99 +
100 + polling();
101 + }
102 +
103 + public void polling() {
104 +
105 + // run asynchronous tasks on background thread to prevent network on main exception
106 + HandlerThread backgroundThread = new HandlerThread("Background Thread");
107 + backgroundThread.start();
108 + Handler timer = new Handler(backgroundThread.getLooper());
109 +
110 + timer.post(new Runnable() {
111 + @Override
112 + public void run() {
113 + HashMap<String, PatientModel> poHashMap = new HashMap<>();
114 +
115 + // loop through all patients. update observations if observation is selected to be monitored
116 + for (PatientModel patientModel: Objects.requireNonNull(selectedPatients.getValue())) {
117 + for (ObservationType type: ObservationType.values()) {
118 + if (patientModel.isObservationMonitored(type)) {
119 + patientModel.setObservation(type, getObservation(patientModel.getPatientID(), type));
120 + poHashMap.put(patientModel.getPatientID(), patientModel);
121 + if (type == ObservationType.BLOOD_PRESSURE) {
122 + patientModel.addLatestBPReadings(observationRepositoryFactory.getLatestBloodPressureReadings(patientModel.getPatientID(),5));
123 + }
124 + }
125 + }
126 + }
127 +
128 + // update LiveData and notify observers
129 + patientObservations.postValue(poHashMap);
130 + timer.postDelayed(this, Integer.parseInt(Objects.requireNonNull(getSelectedFrequency().getValue()))*1000);
131 + }});
132 + }
133 +}
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
3 + android:shape="rectangle">
4 + <solid android:color="@color/unselected" />
5 + <corners android:radius="50dp" />
6 +</shape>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
3 + android:shape="rectangle">
4 + <solid android:color="@color/color_accent" />
5 + <corners android:radius="10dp" />
6 +</shape>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
3 + android:shape="rectangle">
4 + <solid android:color="@color/color_selected_patient" />
5 + <corners android:radius="10dp" />
6 +</shape>
1 +<vector android:height="24dp" android:tint="#FFFFFF"
2 + android:viewportHeight="24.0" android:viewportWidth="24.0"
3 + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
4 + <path android:fillColor="#FF000000" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
5 +</vector>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
3 + xmlns:tools="http://schemas.android.com/tools"
4 + android:layout_width="match_parent"
5 + android:layout_height="wrap_content"
6 + xmlns:card_view="http://schemas.android.com/apk/res-auto"
7 + android:id="@+id/card_layout"
8 + android:foreground="?selectableItemBackground"
9 + card_view:cardBackgroundColor="@color/color_accent"
10 + card_view:cardCornerRadius="12dp"
11 + card_view:cardElevation="3dp"
12 + android:layout_margin="5dp"
13 + >
14 +
15 + <androidx.constraintlayout.widget.ConstraintLayout
16 + android:id="@+id/card_backgroud"
17 + android:layout_width="match_parent"
18 + android:layout_height="94dp">
19 +
20 +
21 + <LinearLayout
22 + android:id="@+id/linear_layout"
23 + android:layout_width="wrap_content"
24 + android:layout_height="wrap_content"
25 + card_view:layout_constraintBottom_toBottomOf="parent"
26 + card_view:layout_constraintEnd_toStartOf="@id/guideline2"
27 + card_view:layout_constraintHorizontal_bias="0"
28 + card_view:layout_constraintStart_toStartOf="parent"
29 + card_view:layout_constraintTop_toTopOf="parent"
30 + card_view:layout_constraintVertical_bias="0.5">
31 +
32 + <CheckBox
33 + android:id="@+id/checkBox"
34 + android:layout_width="wrap_content"
35 + android:layout_height="wrap_content"
36 + android:theme="@style/checkBoxStyle"
37 + android:visibility="gone" />
38 +
39 + </LinearLayout>
40 +
41 +
42 + <TextView
43 + android:id="@+id/all_patient_txt_name"
44 + android:layout_width="0dp"
45 + android:layout_height="wrap_content"
46 + android:text="@string/patient_name"
47 + android:textSize="25sp"
48 + card_view:layout_constraintBottom_toBottomOf="@id/linear_layout"
49 + card_view:layout_constraintEnd_toStartOf="@id/guideline2"
50 + card_view:layout_constraintHorizontal_bias="0.0"
51 + card_view:layout_constraintStart_toEndOf="@id/guideline"
52 + card_view:layout_constraintTop_toTopOf="parent"
53 + card_view:layout_constraintVertical_bias="0.285" />
54 +
55 + <androidx.constraintlayout.widget.Guideline
56 + android:id="@+id/guideline"
57 + android:layout_width="wrap_content"
58 + android:layout_height="wrap_content"
59 + android:orientation="vertical"
60 + card_view:layout_constraintGuide_percent="0.04" />
61 +
62 + <androidx.constraintlayout.widget.Guideline
63 + android:id="@+id/guideline2"
64 + android:layout_width="wrap_content"
65 + android:layout_height="wrap_content"
66 + android:orientation="vertical"
67 + card_view:layout_constraintGuide_percent="0.96" />
68 +
69 + <com.google.android.material.chip.Chip
70 + android:id="@+id/cholesterol_chip"
71 + android:layout_width="wrap_content"
72 + android:layout_height="wrap_content"
73 + android:layout_marginStart="45dp"
74 + android:layout_marginTop="8dp"
75 + android:layout_marginEnd="267dp"
76 + android:checkable="true"
77 + android:text="@string/cholesterol"
78 + card_view:chipIcon="@drawable/cardv_grey_bg"
79 + card_view:layout_constraintBottom_toBottomOf="parent"
80 + card_view:layout_constraintEnd_toEndOf="parent"
81 + card_view:layout_constraintStart_toStartOf="parent"
82 + card_view:layout_constraintTop_toBottomOf="@+id/all_patient_txt_name" />
83 +
84 + <com.google.android.material.chip.Chip
85 + android:id="@+id/blood_pressure_chip"
86 + android:layout_width="wrap_content"
87 + android:layout_height="wrap_content"
88 + android:layout_marginStart="25dp"
89 + android:layout_marginTop="8dp"
90 + android:layout_marginEnd="100dp"
91 + android:checkable="true"
92 + android:text="@string/blood_pressure"
93 + card_view:chipIcon="@drawable/cardv_grey_bg"
94 + card_view:layout_constraintEnd_toEndOf="parent"
95 + card_view:layout_constraintStart_toEndOf="@+id/cholesterol_chip"
96 + card_view:layout_constraintTop_toBottomOf="@+id/all_patient_txt_name" />
97 +
98 +
99 + </androidx.constraintlayout.widget.ConstraintLayout>
100 +
101 +
102 +</androidx.cardview.widget.CardView>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 + android:layout_width="match_parent"
4 + android:layout_height="match_parent"
5 + xmlns:app="http://schemas.android.com/apk/res-auto">
6 +
7 + <include
8 + android:id="@+id/select_patients_toolbar"
9 + layout = "@layout/toolbar"/>
10 +
11 +
12 + <TextView
13 + android:id="@+id/select_patients_txt_loading"
14 + android:layout_width="match_parent"
15 + android:layout_height="match_parent"
16 + android:text="@string/loading_patients"
17 + android:textSize="20sp"
18 + android:layout_marginStart="8dp"
19 + android:layout_marginTop="8dp"
20 + android:layout_marginEnd="8dp"
21 + android:layout_marginBottom="8dp"
22 + app:layout_constraintBottom_toBottomOf="parent"
23 + app:layout_constraintEnd_toEndOf="parent"
24 + app:layout_constraintStart_toStartOf="parent"
25 + app:layout_constraintTop_toBottomOf="@id/select_patients_toolbar"
26 + app:layout_constraintVertical_bias="0"/>
27 +
28 + <ProgressBar
29 + android:id="@+id/select_patients_progressBar"
30 + style="?android:attr/progressBarStyleLarge"
31 + android:layout_width="wrap_content"
32 + android:layout_height="wrap_content"
33 + android:layout_centerHorizontal="true"
34 + app:layout_constraintBottom_toBottomOf="parent"
35 + app:layout_constraintEnd_toEndOf="parent"
36 + app:layout_constraintStart_toStartOf="parent"
37 + app:layout_constraintTop_toBottomOf="@id/select_patients_toolbar"
38 + />
39 +
40 + <androidx.recyclerview.widget.RecyclerView
41 + android:id="@+id/select_patients_recycler_view"
42 + android:layout_width="0dp"
43 + android:layout_height="0dp"
44 + android:layout_marginStart="8dp"
45 + android:layout_marginTop="8dp"
46 + android:layout_marginEnd="8dp"
47 + android:layout_marginBottom="8dp"
48 + app:layout_constraintBottom_toBottomOf="parent"
49 + app:layout_constraintEnd_toEndOf="parent"
50 + app:layout_constraintHorizontal_bias="0.0"
51 + app:layout_constraintStart_toStartOf="parent"
52 + app:layout_constraintTop_toBottomOf="@+id/select_patients_toolbar"
53 + app:layout_constraintVertical_bias="0.0" />
54 +</androidx.constraintlayout.widget.ConstraintLayout>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
3 + xmlns:tools="http://schemas.android.com/tools"
4 + android:layout_width="match_parent"
5 + android:layout_height="wrap_content"
6 + xmlns:card_view="http://schemas.android.com/apk/res-auto"
7 + android:id="@+id/card_layout"
8 + android:foreground="?selectableItemBackground"
9 + card_view:cardBackgroundColor="@color/color_accent"
10 + card_view:cardCornerRadius="12dp"
11 + card_view:cardElevation="3dp"
12 + android:layout_margin="5dp"
13 + >
14 +
15 + <androidx.constraintlayout.widget.ConstraintLayout
16 + android:id="@+id/card_backgroud"
17 + android:layout_width="match_parent"
18 + android:layout_height="94dp">
19 +
20 +
21 + <LinearLayout
22 + android:id="@+id/linear_layout"
23 + android:layout_width="wrap_content"
24 + android:layout_height="wrap_content"
25 + card_view:layout_constraintBottom_toBottomOf="parent"
26 + card_view:layout_constraintEnd_toStartOf="@id/guideline2"
27 + card_view:layout_constraintHorizontal_bias="0"
28 + card_view:layout_constraintStart_toStartOf="parent"
29 + card_view:layout_constraintTop_toTopOf="parent"
30 + card_view:layout_constraintVertical_bias="0.5">
31 +
32 + <CheckBox
33 + android:id="@+id/checkBox"
34 + android:layout_width="wrap_content"
35 + android:layout_height="wrap_content"
36 + android:theme="@style/checkBoxStyle"
37 + android:visibility="gone" />
38 +
39 + </LinearLayout>
40 +
41 +
42 + <TextView
43 + android:id="@+id/all_patient_txt_name"
44 + android:layout_width="0dp"
45 + android:layout_height="wrap_content"
46 + android:text="@string/patient_name"
47 + android:textSize="25sp"
48 + card_view:layout_constraintBottom_toBottomOf="@id/linear_layout"
49 + card_view:layout_constraintEnd_toStartOf="@id/guideline2"
50 + card_view:layout_constraintHorizontal_bias="0.0"
51 + card_view:layout_constraintStart_toEndOf="@id/guideline"
52 + card_view:layout_constraintTop_toTopOf="parent"
53 + card_view:layout_constraintVertical_bias="0.285" />
54 +
55 + <androidx.constraintlayout.widget.Guideline
56 + android:id="@+id/guideline"
57 + android:layout_width="wrap_content"
58 + android:layout_height="wrap_content"
59 + android:orientation="vertical"
60 + card_view:layout_constraintGuide_percent="0.04" />
61 +
62 + <androidx.constraintlayout.widget.Guideline
63 + android:id="@+id/guideline2"
64 + android:layout_width="wrap_content"
65 + android:layout_height="wrap_content"
66 + android:orientation="vertical"
67 + card_view:layout_constraintGuide_percent="0.96" />
68 +
69 + <com.google.android.material.chip.Chip
70 + android:id="@+id/cholesterol_chip"
71 + android:layout_width="wrap_content"
72 + android:layout_height="wrap_content"
73 + android:layout_marginStart="45dp"
74 + android:layout_marginTop="8dp"
75 + android:layout_marginEnd="267dp"
76 + android:checkable="true"
77 + android:text="@string/cholesterol"
78 + card_view:chipIcon="@drawable/cardv_grey_bg"
79 + card_view:layout_constraintBottom_toBottomOf="parent"
80 + card_view:layout_constraintEnd_toEndOf="parent"
81 + card_view:layout_constraintStart_toStartOf="parent"
82 + card_view:layout_constraintTop_toBottomOf="@+id/all_patient_txt_name" />
83 +
84 + <com.google.android.material.chip.Chip
85 + android:id="@+id/blood_pressure_chip"
86 + android:layout_width="wrap_content"
87 + android:layout_height="wrap_content"
88 + android:layout_marginStart="25dp"
89 + android:layout_marginTop="8dp"
90 + android:layout_marginEnd="100dp"
91 + android:checkable="true"
92 + android:text="@string/blood_pressure"
93 + card_view:chipIcon="@drawable/cardv_grey_bg"
94 + card_view:layout_constraintEnd_toEndOf="parent"
95 + card_view:layout_constraintStart_toEndOf="@+id/cholesterol_chip"
96 + card_view:layout_constraintTop_toBottomOf="@+id/all_patient_txt_name" />
97 +
98 +
99 + </androidx.constraintlayout.widget.ConstraintLayout>
100 +
101 +
102 +</androidx.cardview.widget.CardView>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
3 + android:layout_width="match_parent"
4 + android:layout_height="wrap_content"
5 + xmlns:app="http://schemas.android.com/apk/res-auto"
6 + android:background="@color/colorPrimary"
7 + android:elevation="6dp"
8 + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
9 + app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
10 +
11 + <RelativeLayout
12 + android:layout_width="match_parent"
13 + android:layout_height="wrap_content">
14 +
15 + <ImageButton
16 + android:id="@+id/btn_back"
17 + android:layout_width="wrap_content"
18 + android:layout_height="wrap_content"
19 + android:background="@android:color/transparent"
20 + android:src="@drawable/ic_arrow_back_24dp"
21 + android:layout_centerVertical="true"
22 + android:layout_marginRight="20dp"
23 + />
24 +
25 + <TextView
26 + android:id="@+id/toolbar_title"
27 + android:layout_width="wrap_content"
28 + android:layout_height="wrap_content"
29 + android:layout_toRightOf="@id/btn_back"
30 + android:text="Title Here"
31 + android:textSize="23sp"
32 + android:textColor="@color/color_white"
33 + android:layout_centerVertical="true">
34 + </TextView>
35 +
36 + </RelativeLayout>
37 +
38 +</androidx.appcompat.widget.Toolbar>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<menu xmlns:android="http://schemas.android.com/apk/res/android"
3 + xmlns:app="http://schemas.android.com/apk/res-auto">
4 + <item
5 + android:id="@+id/patient_add"
6 + app:showAsAction="always"
7 + android:title="@string/start_monitoring" />
8 +
9 +</menu>
...@@ -368,4 +368,6 @@ ...@@ -368,4 +368,6 @@
368 <string name="string_check_date">检查日期:</string> 368 <string name="string_check_date">检查日期:</string>
369 <string name="string_examination_doctor">检查医生:</string> 369 <string name="string_examination_doctor">检查医生:</string>
370 370
371 +
372 +
371 </resources> 373 </resources>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -61,6 +61,10 @@ ...@@ -61,6 +61,10 @@
61 <color name="color_4f4f4f">#4F4F4F</color> 61 <color name="color_4f4f4f">#4F4F4F</color>
62 <color name="color_565656">#565656</color> 62 <color name="color_565656">#565656</color>
63 <color name="color_737373">#737373</color> 63 <color name="color_737373">#737373</color>
64 - 64 + <color name="color_accent">#E0E9FF</color>
65 + <color name="color_accent_text">#35375A</color>
66 + <color name="color_selected_patient">#bedec5</color>
67 + <color name="unselected">#8c92ac</color>
68 + <color name="colorPrimary">#5C4AE4</color>
65 69
66 </resources> 70 </resources>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -388,4 +388,12 @@ ...@@ -388,4 +388,12 @@
388 <string name="normal_mode">标定模式</string> 388 <string name="normal_mode">标定模式</string>
389 <string name="single_mode">单帧模式</string> 389 <string name="single_mode">单帧模式</string>
390 390
391 + <string name="loading_patients">Loading Patients…</string>
392 + <string name="patient_name">Patient Name</string>
393 + <string name="cholesterol">cholesterol</string>
394 + <string name="blood_pressure">blood pressure</string>
395 + <string name="zero_patients_message">You have 0 patients</string>
396 + <string name="title_patient_selection">Select Patients</string>
397 + <string name="start_monitoring">Start</string>
398 +
391 </resources> 399 </resources>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -53,4 +53,9 @@ ...@@ -53,4 +53,9 @@
53 <item name="colorControlActivated">@color/color_42bf58</item> 53 <item name="colorControlActivated">@color/color_42bf58</item>
54 </style> 54 </style>
55 55
56 + <style name="checkBoxStyle" parent="Base.Theme.AppCompat">
57 + <item name="colorAccent">@color/color_accent_text</item>
58 + <item name="android:textColorSecondary">@color/color_accent_text</item>
59 + </style>
60 +
56 </resources> 61 </resources>
...\ No newline at end of file ...\ No newline at end of file
......