No Description

AssessmentsFragment.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package uprrp.tania.fragments;
  2. import android.app.Activity;
  3. import android.app.ProgressDialog;
  4. import android.content.Intent;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. import android.widget.TextView;
  12. import android.widget.Toast;
  13. import androidx.annotation.Nullable;
  14. import androidx.appcompat.widget.AppCompatButton;
  15. import androidx.fragment.app.Fragment;
  16. import com.google.android.gms.tasks.OnSuccessListener;
  17. import com.google.firebase.iid.FirebaseInstanceId;
  18. import com.google.firebase.iid.InstanceIdResult;
  19. import org.json.JSONException;
  20. import org.json.JSONObject;
  21. import org.researchstack.backbone.StorageAccess;
  22. import org.researchstack.backbone.answerformat.AnswerFormat;
  23. import org.researchstack.backbone.answerformat.ChoiceAnswerFormat;
  24. import org.researchstack.backbone.answerformat.TextAnswerFormat;
  25. import org.researchstack.backbone.model.Choice;
  26. import org.researchstack.backbone.result.StepResult;
  27. import org.researchstack.backbone.result.TaskResult;
  28. import org.researchstack.backbone.step.InstructionStep;
  29. import org.researchstack.backbone.step.QuestionStep;
  30. import org.researchstack.backbone.step.Step;
  31. import org.researchstack.backbone.task.OrderedTask;
  32. import org.researchstack.backbone.ui.ViewTaskActivity;
  33. import java.util.ArrayList;
  34. import java.util.List;
  35. import uprrp.tania.networking.FetchAssessment;
  36. import uprrp.tania.networking.FetchUserStatus;
  37. import uprrp.tania.models.AssessmentModel;
  38. import uprrp.tania.models.QuestionModel;
  39. import uprrp.tania.R;
  40. import uprrp.tania.SendAnswersToServer;
  41. import uprrp.tania.URLEventListener;
  42. import uprrp.tania.models.UserStatusModel;
  43. public class AssessmentsFragment extends Fragment {
  44. // These variables came from the sample app provided to explain the use of Research Stack
  45. private static final int REQUEST_SURVEY = 1;
  46. private static final String INSTRUCTION = "identifier";
  47. private static final String SAMPLE_SURVEY = "sample_assessment";
  48. // Our variables
  49. private final String TAG = "ASSESSMENTS FRAGMENT";
  50. private String device_token;
  51. private String id_subquestionnair;
  52. @Nullable
  53. @Override
  54. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  55. // Get reference for this fragment and other components
  56. final View thisFragment = inflater.inflate(R.layout.fragment_assessments, container, false);
  57. final AppCompatButton surveyButton = thisFragment.findViewById(R.id.surveyButton);
  58. final Button refreshButton = thisFragment.findViewById(R.id.refreshButton); // TODO: check Button vs. AppCompatButton
  59. // Change all caps text to normal capitalization
  60. // TODO: this is a workaround I found, any other acceptable solution is welcome
  61. surveyButton.setTransformationMethod(null);
  62. refreshButton.setTransformationMethod(null);
  63. // Request to Firebase...
  64. FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
  65. @Override
  66. public void onSuccess(InstanceIdResult instanceIdResult) {
  67. // Get device's token
  68. // IMPORTANT: must come first before setting any
  69. device_token = instanceIdResult.getToken();
  70. // Add onClick listener to "Answer Survey" button
  71. surveyButton.setOnClickListener(new View.OnClickListener() {
  72. @Override
  73. public void onClick(View v) {
  74. fetchAssessment();
  75. }
  76. });
  77. // Add onClick listener to "Refresh Status" button
  78. refreshButton.setOnClickListener(new View.OnClickListener() {
  79. @Override
  80. public void onClick(View v) {
  81. fetchUserStatus(thisFragment);
  82. }
  83. });
  84. // Fetch user status and assessment, if any
  85. fetchUserStatus(thisFragment);
  86. // fetchAssessment();
  87. }
  88. });
  89. return thisFragment;
  90. }
  91. private void fetchUserStatus(View thisFragment) {
  92. // Get text fields and initialize other stuff
  93. final TextView statusTextView = thisFragment.findViewById(R.id.statusTextView);
  94. final TextView nextSurveyTextView = thisFragment.findViewById(R.id.nextSurveyTextView);
  95. final Button surveyButton = thisFragment.findViewById(R.id.surveyButton);
  96. final Button refreshButton = thisFragment.findViewById(R.id.refreshButton);
  97. // Change UI to indicate loading is happening
  98. statusTextView.setText(R.string.userStatusLoadingText);
  99. nextSurveyTextView.setVisibility(View.GONE);
  100. // Define task
  101. FetchUserStatus userStatusTask = (FetchUserStatus) new FetchUserStatus(new URLEventListener() {
  102. @Override
  103. public void onSuccess(UserStatusModel userStatus) {
  104. if(userStatus.isEnrolled()) {
  105. // Change next survey date text (as well as status text)
  106. statusTextView.setText(userStatus.toString());
  107. nextSurveyTextView.setText(userStatus.getTimeToNext());
  108. nextSurveyTextView.setVisibility(View.VISIBLE);
  109. // Hide refresh button (it only refreshes user status, anyways so...)
  110. refreshButton.setVisibility(View.GONE);
  111. // Toggle "Answer" button visibility depending on survey availability
  112. // TODO: Decide whether to show the button regardless of anything,
  113. // since this may be optional functionally, but important for UX
  114. if(userStatus.surveyAvailable()) {
  115. surveyButton.setVisibility(View.VISIBLE);
  116. } else {
  117. surveyButton.setVisibility(View.GONE);
  118. }
  119. } else {
  120. // Prompt user to refresh status if user doesn't have an experience
  121. // statusTextView.setText("You are not enrolled in any experience.\nIf you think this is a mistake, contact your supervisors."); // for debugging/testing
  122. statusTextView.setText(userStatus.toString());
  123. nextSurveyTextView.setVisibility(View.GONE);
  124. surveyButton.setVisibility(View.GONE);
  125. refreshButton.setVisibility(View.VISIBLE);
  126. }
  127. }
  128. @Override
  129. public void onFailure(Exception e) {
  130. statusTextView.setText(R.string.userStatusErrorText);
  131. Toast.makeText(getContext(), "Can't verify identity!", Toast.LENGTH_LONG).show();
  132. Log.e("ERROR WHILE FETCHING USER STATUS", e.toString());
  133. }
  134. });
  135. // Start task
  136. userStatusTask.execute(this.device_token);
  137. }
  138. private void fetchAssessment() {
  139. // Initiate progress dialog
  140. // TODO: find substitute for this deprecated dialog box
  141. final ProgressDialog progressDialog = ProgressDialog.show(getContext(),
  142. "Looking for Survey",
  143. "This shouldn't take long");
  144. // Fetch the assessment to be taken (if there is at least one available),
  145. // and launch the assessment (if the button is pressed)
  146. FetchAssessment assessmentTask = (FetchAssessment) new FetchAssessment(new URLEventListener() {
  147. @Override
  148. public void onSuccess(AssessmentModel assessment) {
  149. // Stop progress dialog
  150. progressDialog.dismiss();
  151. // Launch survey or notify there are none available, accordingly
  152. if(assessment.isEmpty()) {
  153. Toast.makeText(getContext(), "There are no surveys at the moment!", Toast.LENGTH_LONG).show();
  154. } else {
  155. launchSurvey(assessment);
  156. }
  157. }
  158. @Override
  159. public void onFailure(Exception e) {
  160. progressDialog.dismiss();
  161. Toast.makeText(getContext(), "Can't reach surveys at the moment!", Toast.LENGTH_LONG).show();
  162. Log.e("ERROR WHILE FETCHING ASSESSMENT", e.toString());
  163. }
  164. });
  165. // Start task
  166. assessmentTask.execute(this.device_token);
  167. }
  168. @Override
  169. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  170. super.onActivityResult(requestCode, resultCode, data);
  171. if(requestCode == REQUEST_SURVEY && resultCode == Activity.RESULT_OK) {
  172. this.processSurveyResult((TaskResult) data.getSerializableExtra(ViewTaskActivity.EXTRA_TASK_RESULT));
  173. }
  174. }
  175. private void launchSurvey(AssessmentModel assessment) {
  176. this.id_subquestionnair = assessment.getID();
  177. List<Step> allSteps = this.prepareSurveySteps(assessment);
  178. OrderedTask task = new OrderedTask(SAMPLE_SURVEY, allSteps);
  179. Intent intent = ViewTaskActivity.newIntent(getContext(), task);
  180. startActivityForResult(intent, REQUEST_SURVEY);
  181. }
  182. private void processSurveyResult(TaskResult result) {
  183. StorageAccess.getInstance().getAppDatabase().saveTaskResult(result);
  184. // Initiate progress dialog
  185. // TODO: find substitute for this deprecated dialog box
  186. final ProgressDialog progressDialog = ProgressDialog.show(getContext(),
  187. "Sending Answers",
  188. "This shouldn't take long");
  189. try {
  190. sendSurveyInfo();
  191. progressDialog.dismiss();
  192. } catch(Exception e) {
  193. // TODO: prompt user to retry sending results
  194. progressDialog.dismiss();
  195. Toast.makeText(getContext(), "Couldn't send answers!", Toast.LENGTH_LONG).show();
  196. Log.e("ERROR WHILE SENDING ANSWERS", e.toString());
  197. e.printStackTrace();
  198. }
  199. }
  200. private void sendSurveyInfo() {
  201. // Load survey results
  202. TaskResult taskResult = StorageAccess.getInstance()
  203. .getAppDatabase()
  204. .loadLatestTaskResult(SAMPLE_SURVEY);
  205. // Serialize results into JSON
  206. String resultsJSON = this.serializeSurveyResults(taskResult);
  207. // Send JSON
  208. Log.d(TAG, "preparedJSON:" + resultsJSON);
  209. new SendAnswersToServer().execute(resultsJSON);
  210. }
  211. private String serializeSurveyResults(TaskResult taskResult) {
  212. JSONObject surveyResults = new JSONObject();
  213. try {
  214. surveyResults.put("os", "Android");
  215. JSONObject data = new JSONObject();
  216. data.put("id_subquestionnair", this.id_subquestionnair);
  217. data.put("token", this.device_token);
  218. // TODO: Try Gson.toJSON() to simplify this process
  219. for (String id : taskResult.getResults().keySet()) {
  220. JSONObject questions = new JSONObject();
  221. StepResult stepResult = taskResult.getStepResult(id);
  222. questions.accumulate("id_question", id);
  223. questions.accumulate("start_datetime", taskResult.getStartDate().toString());
  224. questions.accumulate("question_answer", stepResult.getResult().toString());
  225. questions.accumulate("end_datetime", taskResult.getEndDate().toString());
  226. data.accumulate("preguntas", questions.toString());
  227. }
  228. surveyResults.put("data", data.toString());
  229. } catch (JSONException e) {
  230. e.printStackTrace();
  231. }
  232. return surveyResults.toString();
  233. }
  234. private List<Step> prepareSurveySteps(AssessmentModel assessment) {
  235. // Initialize survey steps
  236. // List<QuestionStep> taniaQuestions = new ArrayList<>();
  237. List<Step> allSteps = new ArrayList<>();
  238. // Create instruction screen (add it to total steps)
  239. InstructionStep instructionStep = new InstructionStep(INSTRUCTION,
  240. getString(R.string.app_name),
  241. assessment.getDescription());
  242. instructionStep.setStepTitle(R.string.survey);
  243. allSteps.add(0, instructionStep);
  244. // Prepare survey question by question
  245. List<QuestionModel> questions = assessment.getQuestions();
  246. Log.d(TAG, "AssessmentCount: " + assessment.getQuestionCount() + " QuestionCount: " + questions.size()); // just to be sure
  247. for(int i = 0; i < assessment.getQuestionCount(); i++) {
  248. QuestionModel question = questions.get(i);
  249. Log.d("id_type", question.getType());
  250. if(question.getType().equals("SCALED")) {
  251. // Create question choices
  252. Choice[] choices = new Choice[question.getMaxValue()];
  253. for(int m = 0; m < question.getMaxValue(); m++) {
  254. if (m == 0) {
  255. choices[m] = new Choice<>((m + 1) + " " + question.getMinText(), m + 1);
  256. } else if (m == question.getMaxValue() - 1) {
  257. choices[m] = new Choice<>((m + 1) + " " + question.getMaxText(), m + 1);
  258. } else {
  259. choices[m] = new Choice<>(Integer.toString(m + 1), m + 1);
  260. }
  261. }
  262. // Create question step along with its choices
  263. QuestionStep newQuestionStep = new QuestionStep(question.getID());
  264. AnswerFormat multiFormat = new ChoiceAnswerFormat(AnswerFormat.ChoiceAnswerStyle.SingleChoice, choices);
  265. // Set question step details
  266. newQuestionStep.setStepTitle(R.string.survey);
  267. newQuestionStep.setTitle(question.getPremise());
  268. newQuestionStep.setAnswerFormat(multiFormat);
  269. newQuestionStep.setOptional(false);
  270. // Add step to arrays
  271. // taniaQuestions.add(i, newQuestionStep);
  272. allSteps.add(newQuestionStep);
  273. } else if(question.getType().equals("OPEN")) {
  274. // Create question step along with its choices
  275. QuestionStep newQuestionStep = new QuestionStep(question.getID());
  276. TextAnswerFormat format = new TextAnswerFormat();
  277. // Set question step details
  278. newQuestionStep.setStepTitle(R.string.survey);
  279. newQuestionStep.setTitle(question.getPremise());
  280. newQuestionStep.setAnswerFormat(format);
  281. newQuestionStep.setOptional(false);
  282. // Add step to arrays
  283. // taniaQuestions.add(i, newQuestionStep);
  284. allSteps.add(newQuestionStep);
  285. }
  286. }
  287. return allSteps;
  288. }
  289. }