Browse Source

ExperienceRegistration got a makeover. Fixes scaled questions bug. Implements 'Retry/Cancel' logic when sending survey answers. Adds multiline open answers (yet limits them to database limit). Heavy refactoring once more.

Víctor Hernández 3 years ago
parent
commit
905ee8db9c
23 changed files with 885 additions and 630 deletions
  1. BIN
      .idea/caches/build_file_checksums.ser
  2. 1
    0
      app/build.gradle
  3. 0
    95
      app/src/main/java/uprrp/tania/SendActivateExperienceToServer.java
  4. 0
    71
      app/src/main/java/uprrp/tania/SendAnswersToServer.java
  5. 0
    118
      app/src/main/java/uprrp/tania/SendWithdrawalToServer.java
  6. 4
    4
      app/src/main/java/uprrp/tania/URLEventListener.java
  7. 64
    81
      app/src/main/java/uprrp/tania/activities/ExperienceRegistrationActivity.java
  8. 43
    66
      app/src/main/java/uprrp/tania/activities/MainActivity.java
  9. 152
    108
      app/src/main/java/uprrp/tania/fragments/AssessmentsFragment.java
  10. 12
    17
      app/src/main/java/uprrp/tania/fragments/ConsentFragment.java
  11. 111
    44
      app/src/main/java/uprrp/tania/fragments/WithdrawFragment.java
  12. 16
    0
      app/src/main/java/uprrp/tania/models/AnsweredAssessmentModel.java
  13. 16
    0
      app/src/main/java/uprrp/tania/models/AnsweredQuestionModel.java
  14. 2
    2
      app/src/main/java/uprrp/tania/models/QuestionModel.java
  15. 7
    0
      app/src/main/java/uprrp/tania/models/UserStatusModel.java
  16. 17
    9
      app/src/main/java/uprrp/tania/networking/FetchAssessment.java
  17. 21
    11
      app/src/main/java/uprrp/tania/networking/FetchUserStatus.java
  18. 163
    0
      app/src/main/java/uprrp/tania/networking/SendAnswers.java
  19. 126
    0
      app/src/main/java/uprrp/tania/networking/SendExperienceRegistration.java
  20. 116
    0
      app/src/main/java/uprrp/tania/networking/SendWithdrawal.java
  21. 1
    1
      app/src/main/res/layout/fragment_assessments.xml
  22. 12
    2
      app/src/main/res/values/strings.xml
  23. 1
    1
      build.gradle

BIN
.idea/caches/build_file_checksums.ser View File


+ 1
- 0
app/build.gradle View File

@@ -66,6 +66,7 @@ dependencies {
66 66
     implementation 'androidx.navigation:navigation-ui:2.2.1'
67 67
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
68 68
     implementation 'androidx.appcompat:appcompat:1.1.0'
69
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
69 70
     testImplementation 'junit:junit:4.12'
70 71
 
71 72
     implementation 'com.android.support:support-v4:28.0.0'

+ 0
- 95
app/src/main/java/uprrp/tania/SendActivateExperienceToServer.java View File

@@ -1,95 +0,0 @@
1
-package uprrp.tania;
2
-
3
-import android.os.AsyncTask;
4
-import android.util.Log;
5
-
6
-import java.io.BufferedReader;
7
-import java.io.InputStreamReader;
8
-import java.io.OutputStreamWriter;
9
-import java.io.UnsupportedEncodingException;
10
-import java.net.URL;
11
-import java.net.URLEncoder;
12
-
13
-import javax.net.ssl.HttpsURLConnection;
14
-
15
-public class SendActivateExperienceToServer extends AsyncTask<String, String, String> {
16
-
17
-    private final String TAG = "SendActivateExperience";
18
-    private final URLEventListener myCallBack;
19
-    private URLEventListener myCallback;
20
-
21
-    public SendActivateExperienceToServer(URLEventListener callback) {
22
-        this.myCallBack = callback;
23
-    }
24
-
25
-    @Override
26
-    protected String doInBackground(String... strings) {
27
-
28
-        String experienceRegistrationJSON = strings[0]; // array will only ever contain a single element
29
-        String jsonActivateExperience = "";
30
-
31
-        // Encode data
32
-        try {
33
-            jsonActivateExperience = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(experienceRegistrationJSON, "UTF-8");
34
-        } catch (UnsupportedEncodingException e) {
35
-            Log.e(TAG, "Couldn't encode the following JSON: " + experienceRegistrationJSON);
36
-            e.printStackTrace();
37
-            return null;
38
-        }
39
-
40
-        // Send data
41
-        try {
42
-
43
-            // Send POST data request
44
-            URL url = new URL("https://tania.uprrp.edu/inscripcionExperiencia.php"); // TODO: extract URL into @string/activateExperienceURL
45
-            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
46
-            conn.setDoOutput(true);
47
-            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
48
-            wr.write(jsonActivateExperience);
49
-            wr.flush();
50
-
51
-            // Get the server response
52
-            BufferedReader serverReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
53
-            StringBuilder serverResponse = new StringBuilder();
54
-
55
-            String line = "";
56
-            while(line != null) {
57
-                serverResponse.append(line);
58
-                line = serverReader.readLine();
59
-            }
60
-
61
-            String response = serverResponse.toString();
62
-            if(response.startsWith("Success")) {
63
-                return response;
64
-            } else {
65
-                return null;
66
-            }
67
-
68
-
69
-        } catch(Exception e) {
70
-            Log.e("Couldn't communicate with server while activating experience!", e.getMessage());
71
-        }
72
-
73
-        return null;
74
-
75
-    }
76
-
77
-    @Override
78
-    protected void onPostExecute(String response) {
79
-
80
-        if(this.myCallBack == null) {
81
-            this.myCallBack.onFailure(new Exception("Callback wasn't initialized first!"));
82
-            return;
83
-        }
84
-
85
-        if(response == null) {
86
-            this.myCallback.onFailure(new Exception("Error occurred during transaction!"));
87
-            return;
88
-        }
89
-
90
-        Log.d("ACTIVATE EXPERIENCE RESPONSE", "The server's response is: " + response);
91
-        this.myCallBack.onSuccess();
92
-
93
-    }
94
-
95
-}

+ 0
- 71
app/src/main/java/uprrp/tania/SendAnswersToServer.java View File

@@ -1,71 +0,0 @@
1
-/*************************************************************
2
- * By: Coralys Cubero Rivera
3
- * Date: 2019
4
- *************************************************************/
5
-
6
-package uprrp.tania;
7
-
8
-import android.os.AsyncTask;
9
-import android.util.Log;
10
-import java.io.BufferedReader;
11
-import java.io.InputStreamReader;
12
-import java.io.OutputStreamWriter;
13
-import java.io.UnsupportedEncodingException;
14
-import java.net.URL;
15
-import java.net.URLConnection;
16
-import java.net.URLEncoder;
17
-
18
-public class SendAnswersToServer extends AsyncTask <String, String, String> {
19
-    @Override
20
-    protected String doInBackground(String... strings) {
21
-        String jsonSurveyAnswers  = " ";
22
-        try {
23
-            jsonSurveyAnswers = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(strings[0], "UTF-8");
24
-        } catch (UnsupportedEncodingException e) {
25
-            e.printStackTrace();
26
-        }
27
-
28
-        String serverReply = "";
29
-        BufferedReader serverReader;
30
-
31
-        //Send data
32
-        try
33
-        {
34
-            //Defined URL  where to send data
35
-            URL url = new URL("https://tania.uprrp.edu/parseAnswers.php");
36
-
37
-            // Send POST data request
38
-            URLConnection conn = url.openConnection();
39
-            conn.setDoOutput(true);
40
-            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
41
-            wr.write(jsonSurveyAnswers);
42
-            wr.flush();
43
-
44
-            // Get the server response
45
-            serverReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
46
-            StringBuilder serverResponse = new StringBuilder();
47
-            String serverResponseLine;
48
-
49
-            // Read Server Response
50
-            while((serverResponseLine = serverReader.readLine()) != null)
51
-            {
52
-                // Append server response in string
53
-                serverResponse.append(serverResponseLine);
54
-            }
55
-
56
-            serverReply = serverResponse.toString();
57
-        }
58
-        catch(Exception ex) {
59
-            Log.e("ERROR SURVEYS ANSWERS", ex.getMessage());
60
-        }
61
-
62
-        return serverReply;
63
-    }
64
-
65
-    @Override
66
-    protected void onPostExecute(String s) {
67
-        Log.d("SURVEYS ANSWERS REPLY", s);
68
-
69
-    }
70
-
71
-}

+ 0
- 118
app/src/main/java/uprrp/tania/SendWithdrawalToServer.java View File

@@ -1,118 +0,0 @@
1
-/*************************************************************
2
- * By: Coralys Cubero Rivera
3
- * Date: 2019
4
- *************************************************************/
5
-
6
-package uprrp.tania;
7
-
8
-import android.content.Context;
9
-import android.content.Intent;
10
-import android.content.SharedPreferences;
11
-import android.os.AsyncTask;
12
-import android.util.Log;
13
-
14
-import java.io.BufferedReader;
15
-import java.io.BufferedWriter;
16
-import java.io.InputStream;
17
-import java.io.InputStreamReader;
18
-import java.io.OutputStreamWriter;
19
-import java.io.UnsupportedEncodingException;
20
-import java.io.Writer;
21
-import java.net.URL;
22
-import java.net.URLConnection;
23
-import java.net.URLEncoder;
24
-import java.nio.charset.StandardCharsets;
25
-
26
-import uprrp.tania.activities.GettingStartedActivity;
27
-
28
-public class SendWithdrawalToServer extends AsyncTask<String, String, String> {
29
-
30
-    private Context context;
31
-
32
-    public SendWithdrawalToServer(Context context){
33
-        this.context = context.getApplicationContext();
34
-    }
35
-    @Override
36
-    protected String doInBackground(String... strings) {
37
-        String jsonWithdraw  = "";
38
-
39
-        try {
40
-            jsonWithdraw = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(strings[0], "UTF-8");
41
-
42
-        }
43
-
44
-        catch (UnsupportedEncodingException e) {
45
-            e.printStackTrace();
46
-        }
47
-
48
-        String serverReply = "";
49
-        BufferedReader serverReader;
50
-
51
-        //Send the signature to TANIA's server
52
-        try
53
-        {
54
-            //Defined URL  where to send data
55
-            URL url = new URL("https://tania.uprrp.edu/withdrawal.php");
56
-
57
-            //Send POST data request
58
-            URLConnection conn = url.openConnection();
59
-            conn.setDoOutput(true);
60
-
61
-            Writer writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
62
-            writer.write(jsonWithdraw);
63
-            writer.close();
64
-
65
-            InputStream inputStream = conn.getInputStream();
66
-
67
-            if (inputStream == null) {
68
-                return null;
69
-            }
70
-
71
-            //Get the server response
72
-            serverReader = new BufferedReader(new InputStreamReader(inputStream));
73
-            StringBuilder serverResponse = new StringBuilder();
74
-            String serverResponseLine;
75
-
76
-            // Read Server Response
77
-            while((serverResponseLine = serverReader.readLine()) != null)
78
-            {
79
-                //Append server response in string
80
-                serverResponse.append(serverResponseLine);
81
-            }
82
-
83
-            serverReply = serverResponse.toString();
84
-        }
85
-        catch(Exception ex) {
86
-            Log.e("ERROR WITHDRAWAL", ex.getMessage());
87
-        }
88
-
89
-        return serverReply;
90
-    }
91
-
92
-    @Override
93
-    protected void onPostExecute(String s) {
94
-        Log.d("WITHDRAWAL SERVER REPLY", s);
95
-
96
-        String ss = s.substring(0,6);
97
-        String se = s.substring(0,7);
98
-        if (ss.equals("Succes")){
99
-
100
-            SharedPreferences prefs = context.getSharedPreferences("prefs", Context.MODE_PRIVATE);
101
-            SharedPreferences.Editor editor = prefs.edit();
102
-            editor.putBoolean("needsToRegister", true);
103
-            editor.apply();
104
-
105
-            try {
106
-                Intent intent = new Intent(context, GettingStartedActivity.class);
107
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
108
-                context.startActivity(intent);
109
-            }
110
-            catch (Exception e){
111
-                Log.d("WITHDRAWAL TO GETTING STARTED", e.getMessage());
112
-            }
113
-
114
-
115
-        }
116
-
117
-    }
118
-}

+ 4
- 4
app/src/main/java/uprrp/tania/URLEventListener.java View File

@@ -4,8 +4,8 @@ import uprrp.tania.models.AssessmentModel;
4 4
 import uprrp.tania.models.UserStatusModel;
5 5
 
6 6
 public class URLEventListener {
7
-    public void onSuccess() {};
8
-    public void onSuccess(AssessmentModel assessment) {};
9
-    public void onSuccess(UserStatusModel userStatus) {};
10
-    public void onFailure(Exception e) {};
7
+    public void onSuccess() {}
8
+    public void onSuccess(AssessmentModel assessment) {}
9
+    public void onSuccess(UserStatusModel userStatus) {}
10
+    public void onFailure(Exception e) {}
11 11
 }

+ 64
- 81
app/src/main/java/uprrp/tania/activities/ExperienceRegistrationActivity.java View File

@@ -5,7 +5,6 @@ import android.content.Context;
5 5
 import android.content.Intent;
6 6
 import android.net.Uri;
7 7
 import android.os.Bundle;
8
-import android.os.Handler;
9 8
 import android.util.Log;
10 9
 import android.view.View;
11 10
 import android.widget.Button;
@@ -18,18 +17,15 @@ import com.google.android.gms.tasks.OnSuccessListener;
18 17
 import com.google.firebase.iid.FirebaseInstanceId;
19 18
 import com.google.firebase.iid.InstanceIdResult;
20 19
 
21
-import org.json.JSONException;
22
-import org.json.JSONObject;
23
-
24 20
 import uprrp.tania.R;
25
-import uprrp.tania.SendActivateExperienceToServer;
21
+import uprrp.tania.networking.SendExperienceRegistration;
26 22
 import uprrp.tania.URLEventListener;
27 23
 
28 24
 public class ExperienceRegistrationActivity  extends AppCompatActivity {
29 25
 
30
-    private final String TAG = "ExperienceRegistrationActivity";
31
-    private String device_token;
32
-    private String id_experience;
26
+    private static final String TAG = "ExperienceRegistrationActivity";
27
+    private String DEVICE_TOKEN;
28
+    private String EXPERIENCE_ID;
33 29
 
34 30
     @Override
35 31
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -44,90 +40,24 @@ public class ExperienceRegistrationActivity  extends AppCompatActivity {
44 40
         experienceRegistrationButton.setTransformationMethod(null);
45 41
 
46 42
         // Request to Firebase...
43
+        // TODO: Should we implement retry logic in onFailureListener?
47 44
         FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
48 45
             @Override
49 46
             public void onSuccess(InstanceIdResult instanceIdResult) {
50 47
 
51 48
             // Get device's token
52
-            device_token = instanceIdResult.getToken();
49
+            // IMPORTANT: must come first before calling
50
+            // another function that uses the token
51
+            DEVICE_TOKEN = instanceIdResult.getToken();
53 52
 
54 53
             // Set onClick listener on button
55 54
             experienceRegistrationButton.setOnClickListener(new View.OnClickListener() {
56 55
                 @Override
57 56
                 public void onClick(View v) {
58
-
59
-                    // Initiate progress dialog
60
-                    // TODO: find substitute for this deprecated dialog box
61
-                    final ProgressDialog progressDialog = ProgressDialog.show(ExperienceRegistrationActivity.this,
62
-                            "Registering in Experience",
63
-                            "This shouldn't take long");
64
-
65
-                    if(!id_experience.equals("") && id_experience != null) {
66
-                        try {
67
-
68
-                            // Create JSON with details we'll send
69
-                            JSONObject experienceCodeJSON = new JSONObject();
70
-                            experienceCodeJSON.put("token", device_token);
71
-                            experienceCodeJSON.put("id_experiencia", id_experience);
72
-                            Log.d("EXPERIENCE CODE JSON", experienceCodeJSON.toString());
73
-
74
-                            // Send registration request to server
75
-                            SendActivateExperienceToServer experienceRegistrationTask = new SendActivateExperienceToServer(new URLEventListener() {
76
-                                @Override
77
-                                public void onSuccess() {
78
-                                    progressDialog.dismiss();
79
-                                    Context context = ExperienceRegistrationActivity.this;
80
-                                    Intent intent = new Intent(context, MainActivity.class);
81
-                                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
82
-                                    context.startActivity(intent);
83
-                                    Toast.makeText(getApplicationContext(), "You have been registered!", Toast.LENGTH_LONG).show();
84
-                                }
85
-
86
-                                @Override
87
-                                public void onFailure(Exception e) {
88
-                                    progressDialog.dismiss();
89
-                                    // TODO: restrict double registration (maybe in backend?)
90
-                                    Toast.makeText(ExperienceRegistrationActivity.this, "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
91
-                                    Log.e(TAG, "Error occurred while sending registration request to server...");
92
-                                    e.printStackTrace();
93
-                                }
94
-                            });
95
-
96
-                            experienceRegistrationTask.execute(experienceCodeJSON.toString());
97
-
98
-                            // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
99
-                            /*
100
-                            Handler handler = new Handler();
101
-                            handler.postDelayed(new Runnable() {
102
-                                public void run() {
103
-                                    progressDialog.dismiss();
104
-                                    if(false) { // Imitate success
105
-                                        Context context = ExperienceRegistrationActivity.this;
106
-                                        Intent intent = new Intent(context, MainActivity.class);
107
-                                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
108
-                                        context.startActivity(intent);
109
-                                        Toast.makeText(getApplicationContext(), "You have been registered!", Toast.LENGTH_LONG).show();
110
-                                    } else { // Imitate failure
111
-                                        Toast.makeText(ExperienceRegistrationActivity.this, "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
112
-                                    }
113
-                                }
114
-                            }, 5000);
115
-                            */
116
-                            // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
117
-
118
-                        } catch (JSONException e) {
119
-                            progressDialog.dismiss();
120
-                            Toast.makeText(ExperienceRegistrationActivity.this, "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
121
-                            Log.e(TAG, "Error occurred while preparing JSON for server");
122
-                            e.printStackTrace();
123
-                        }
124
-                    } else {
125
-                        progressDialog.dismiss();
126
-                        Toast.makeText(ExperienceRegistrationActivity.this, "Invalid URL!", Toast.LENGTH_LONG).show();
127
-                        Log.d(TAG, "Invalid query parameter found (" + id_experience + ")");
128
-                    }
57
+                    sendRegistrationRequest();
129 58
                 }
130 59
             });
60
+
131 61
             }
132 62
         });
133 63
 
@@ -136,9 +66,62 @@ public class ExperienceRegistrationActivity  extends AppCompatActivity {
136 66
         if(Intent.ACTION_VIEW.equals(intent.getAction())) {
137 67
             Uri uri = intent.getData();
138 68
             assert uri != null; // TODO: figure out if this causes crashes...
139
-            id_experience = uri.getQueryParameter("id");
69
+            EXPERIENCE_ID = uri.getQueryParameter("id");
140 70
         }
141 71
 
142 72
     }
143 73
 
74
+    private void sendRegistrationRequest() {
75
+
76
+        // Initiate progress dialog
77
+        // TODO: find substitute for this deprecated dialog box
78
+        final ProgressDialog progressDialog = ProgressDialog.show(ExperienceRegistrationActivity.this,
79
+                "Registering in Experience",
80
+                "This shouldn't take long");
81
+
82
+        // Send registration request to server
83
+        SendExperienceRegistration experienceRegistrationTask = new SendExperienceRegistration(new URLEventListener() {
84
+            @Override
85
+            public void onSuccess() {
86
+                progressDialog.dismiss();
87
+                Context context = ExperienceRegistrationActivity.this;
88
+                Intent intent = new Intent(context, MainActivity.class);
89
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
90
+                context.startActivity(intent);
91
+                Toast.makeText(getApplicationContext(), "You have been registered!", Toast.LENGTH_LONG).show();
92
+            }
93
+
94
+            @Override
95
+            public void onFailure(Exception e) {
96
+                progressDialog.dismiss();
97
+                Toast.makeText(ExperienceRegistrationActivity.this, "Oops! Something went wrong...\nPlease try again", Toast.LENGTH_LONG).show();
98
+                Log.e(TAG, "Error occurred while sending registration request to server...");
99
+                e.printStackTrace();
100
+            }
101
+        });
102
+
103
+        experienceRegistrationTask.execute(this.DEVICE_TOKEN, this.EXPERIENCE_ID);
104
+
105
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
106
+        /*
107
+        Handler handler = new Handler();
108
+        handler.postDelayed(new Runnable() {
109
+            public void run() {
110
+                progressDialog.dismiss();
111
+                if(false) { // Imitate success
112
+                    Context context = ExperienceRegistrationActivity.this;
113
+                    Intent intent = new Intent(context, MainActivity.class);
114
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
115
+                    context.startActivity(intent);
116
+                    Toast.makeText(getApplicationContext(), "You have been registered!", Toast.LENGTH_LONG).show();
117
+                } else { // Imitate failure
118
+                    Toast.makeText(ExperienceRegistrationActivity.this, "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
119
+                }
120
+            }
121
+        }, 5000);
122
+        */
123
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
124
+
125
+    }
126
+
144 127
 }

+ 43
- 66
app/src/main/java/uprrp/tania/activities/MainActivity.java View File

@@ -1,8 +1,3 @@
1
-/*************************************************************
2
- * By: Coralys Cubero Rivera
3
- * Date: 2019
4
- *************************************************************/
5
-
6 1
 package uprrp.tania.activities;
7 2
 
8 3
 import android.content.Intent;
@@ -32,97 +27,79 @@ import uprrp.tania.fragments.AssessmentsFragment;
32 27
 import uprrp.tania.fragments.ConsentFragment;
33 28
 import uprrp.tania.fragments.WithdrawFragment;
34 29
 
35
-
36 30
 public class MainActivity extends PinCodeActivity {
37 31
 
38
-    String device_token;
32
+    private static final String TAG = "MainActivity";
33
+
39 34
     @Override
40
-    protected void onCreate(Bundle savedInstanceState) {
35
+    protected void onCreate(final Bundle savedInstanceState) {
36
+
37
+        // Constructor stuff
41 38
         super.onCreate(savedInstanceState);
42 39
         setContentView(R.layout.activity_main);
43 40
 
44
-        // If the application was opened using the URL scheme
45
-        Intent intent = getIntent();
46
-        if (intent != null && intent.getData() != null) {
47
-            Uri uri = intent.getData();
48
-            String expToken = null;
49
-            if (uri != null) {
50
-                expToken = uri.getQueryParameter("expToken");
51
-            }
52
-            Log.d("URL SCHEME", "The experience token is " + expToken);
53
-            //SendActivateExperienceToServer activateExperience = new SendActivateExperienceToServer();
54
-            //activateExperience.execute(expToken);
55
-        }
56
-        /////////////////////////////////////////////////////////////////////////////
57 41
 
58 42
         // We need to know if the user has already registered, otherwise they need to do that first
59 43
         SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
60 44
         boolean needsToRegister = prefs.getBoolean("needsToRegister", true);
61
-
62
-        if (needsToRegister){
63
-           Intent needsRegisterIntent = new Intent(this, GettingStartedActivity.class);
45
+        if(needsToRegister) {
46
+            Intent needsRegisterIntent = new Intent(this, GettingStartedActivity.class);
64 47
             startActivity(needsRegisterIntent);
65
-         }
66
-        /////////////////////////////////////////////////////////////////////////////
67
-
48
+        }
68 49
 
69
-        //////////////////// Let's get NOTIFICATIONS //////////////////////////
70
-        FirebaseInstanceId.getInstance().getInstanceId()
71
-                .addOnSuccessListener(MainActivity.this, new OnSuccessListener<InstanceIdResult>() {
72
-                    @Override
73
-                    public void onSuccess(InstanceIdResult instanceIdResult) {
74
-                        device_token = instanceIdResult.getToken();
75
-                    }
76
-                });
77 50
 
51
+        // Enable notifications
78 52
         CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
79 53
                 getApplicationContext(),
80
-                "us-east-1:574094cd-0784-4e26-bd14-4fa72ae63579", // Identity pool ID
54
+                getString(R.string.identityPoolID), // Identity pool ID
81 55
                 Regions.US_EAST_1 // Region
82 56
         );
83
-
84 57
         SNSRegister snsRegister = new SNSRegister(credentialsProvider);
85 58
         snsRegister.execute();
86
-        ////////////////////////////////////////////////////////////////
87 59
 
88 60
 
61
+        // Create toolbar
89 62
         Toolbar toolbar = findViewById(R.id.toolbar);
90 63
         setSupportActionBar(toolbar);
91 64
         ActionBar actionBar = getSupportActionBar();
92
-        assert actionBar != null;
93
-        actionBar.setTitle("TANIA");
65
+        assert actionBar != null; // TODO: find out if this causes crashes
66
+        actionBar.setTitle(R.string.app_name);
94 67
         actionBar.setDisplayShowTitleEnabled(true);
95 68
 
69
+
70
+        // Set navigation buttons listener
96 71
         BottomNavigationView bottomNavigationMenu = findViewById(R.id.bottom_navigation_menu);
97
-        bottomNavigationMenu.setOnNavigationItemSelectedListener(navListener);
72
+        bottomNavigationMenu.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
73
+            @Override
74
+            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
75
+
76
+                Fragment selectedFragment;
77
+
78
+                switch (item.getItemId()) {
79
+                    case R.id.nav_assessments:
80
+                        selectedFragment = new AssessmentsFragment();
81
+                        break;
82
+                    case R.id.nav_consent:
83
+                        selectedFragment = new ConsentFragment();
84
+                        break;
85
+                    case R.id.nav_settings:
86
+                        selectedFragment = new WithdrawFragment();
87
+                        break;
88
+                    default:
89
+                        selectedFragment = new Fragment();
90
+                }
98 91
 
92
+                getSupportFragmentManager()
93
+                        .beginTransaction()
94
+                        .replace(R.id.mainFragment, selectedFragment)
95
+                        .commit();
99 96
 
100
-    }
97
+                return true;
101 98
 
102
-    private BottomNavigationView.OnNavigationItemSelectedListener navListener =
103
-            new BottomNavigationView.OnNavigationItemSelectedListener() {
104
-                @Override
105
-                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
106
-                    Fragment selectedFragment = new Fragment();
107
-
108
-                    switch (item.getItemId()) {
109
-                        case R.id.nav_assessments:
110
-                            selectedFragment = new AssessmentsFragment();
111
-                            break;
112
-                        case R.id.nav_consent:
113
-                            selectedFragment = new ConsentFragment();
114
-                            break;
115
-                        case R.id.nav_settings:
116
-                            selectedFragment = new WithdrawFragment();
117
-                            break;
118
-                    }
119
-
120
-                    getSupportFragmentManager().beginTransaction().replace(R.id.mainFragment,
121
-                            selectedFragment).commit();
122
-
123
-                    return true;
124
-                }
125
-            };
99
+            }
100
+        });
101
+
102
+    }
126 103
 
127 104
 }
128 105
 

+ 152
- 108
app/src/main/java/uprrp/tania/fragments/AssessmentsFragment.java View File

@@ -2,6 +2,7 @@ package uprrp.tania.fragments;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.app.ProgressDialog;
5
+import android.content.DialogInterface;
5 6
 import android.content.Intent;
6 7
 import android.os.Bundle;
7 8
 import android.util.Log;
@@ -12,15 +13,13 @@ import android.widget.Button;
12 13
 import android.widget.TextView;
13 14
 import android.widget.Toast;
14 15
 import androidx.annotation.Nullable;
15
-import androidx.appcompat.widget.AppCompatButton;
16
+import androidx.appcompat.app.AlertDialog;
16 17
 import androidx.fragment.app.Fragment;
17 18
 
18 19
 import com.google.android.gms.tasks.OnSuccessListener;
19 20
 import com.google.firebase.iid.FirebaseInstanceId;
20 21
 import com.google.firebase.iid.InstanceIdResult;
21 22
 
22
-import org.json.JSONException;
23
-import org.json.JSONObject;
24 23
 import org.researchstack.backbone.StorageAccess;
25 24
 import org.researchstack.backbone.answerformat.AnswerFormat;
26 25
 import org.researchstack.backbone.answerformat.ChoiceAnswerFormat;
@@ -36,13 +35,16 @@ import org.researchstack.backbone.ui.ViewTaskActivity;
36 35
 
37 36
 import java.util.ArrayList;
38 37
 import java.util.List;
38
+import java.util.Objects;
39 39
 
40
+import uprrp.tania.networking.SendAnswers;
41
+import uprrp.tania.models.AnsweredQuestionModel;
42
+import uprrp.tania.models.AnsweredAssessmentModel;
40 43
 import uprrp.tania.networking.FetchAssessment;
41 44
 import uprrp.tania.networking.FetchUserStatus;
42 45
 import uprrp.tania.models.AssessmentModel;
43 46
 import uprrp.tania.models.QuestionModel;
44 47
 import uprrp.tania.R;
45
-import uprrp.tania.SendAnswersToServer;
46 48
 import uprrp.tania.URLEventListener;
47 49
 import uprrp.tania.models.UserStatusModel;
48 50
 
@@ -54,18 +56,19 @@ public class AssessmentsFragment extends Fragment {
54 56
     private static final String SAMPLE_SURVEY = "sample_assessment";
55 57
 
56 58
     // Our variables
57
-    private final String TAG = "ASSESSMENTS FRAGMENT";
58
-    private String device_token;
59
-    private String id_subquestionnair;
59
+    private static final String TAG = "AssessmentsFragment";
60
+    private String DEVICE_TOKEN;
61
+    private String ASSESSMENT_ID;
62
+    private View thisFragment;
60 63
 
61 64
     @Nullable
62 65
     @Override
63 66
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
64 67
 
65 68
         // Get reference for this fragment and other components
66
-        final View thisFragment = inflater.inflate(R.layout.fragment_assessments, container, false);
67
-        final AppCompatButton surveyButton = thisFragment.findViewById(R.id.surveyButton);
68
-        final Button refreshButton = thisFragment.findViewById(R.id.refreshButton); // TODO: check Button vs. AppCompatButton
69
+        this.thisFragment = inflater.inflate(R.layout.fragment_assessments, container, false);
70
+        final Button surveyButton = thisFragment.findViewById(R.id.surveyButton);
71
+        final Button refreshButton = thisFragment.findViewById(R.id.refreshButton);
69 72
 
70 73
         // Change all caps text to normal capitalization
71 74
         // TODO: this is a workaround I found, any other acceptable solution is welcome
@@ -73,32 +76,34 @@ public class AssessmentsFragment extends Fragment {
73 76
         refreshButton.setTransformationMethod(null);
74 77
 
75 78
         // Request to Firebase...
79
+        // TODO: Should we implement retry logic in onFailureListener?
76 80
         FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
77 81
             @Override
78 82
             public void onSuccess(InstanceIdResult instanceIdResult) {
79 83
 
80
-            // Get device's token
81
-            // IMPORTANT: must come first before setting any
82
-            device_token = instanceIdResult.getToken();
84
+                // Get device's token
85
+                // IMPORTANT: must come first before calling
86
+                // another function that uses the token
87
+                DEVICE_TOKEN = instanceIdResult.getToken();
83 88
 
84
-            // Add onClick listener to "Answer Survey" button
85
-            surveyButton.setOnClickListener(new View.OnClickListener() {
86
-                @Override
87
-                public void onClick(View v) {
88
-                    fetchAssessment();
89
-                }
90
-            });
89
+                // Add onClick listener to "Answer Survey" button
90
+                surveyButton.setOnClickListener(new View.OnClickListener() {
91
+                    @Override
92
+                    public void onClick(View v) {
93
+                        fetchAssessment();
94
+                    }
95
+                });
91 96
 
92
-            // Add onClick listener to "Refresh Status" button
93
-            refreshButton.setOnClickListener(new View.OnClickListener() {
94
-                @Override
95
-                public void onClick(View v) {
96
-                    fetchUserStatus(thisFragment);
97
-                }
98
-            });
97
+                // Add onClick listener to "Refresh Status" button
98
+                refreshButton.setOnClickListener(new View.OnClickListener() {
99
+                    @Override
100
+                    public void onClick(View v) {
101
+                        fetchUserStatus();
102
+                    }
103
+                });
99 104
 
100
-            // Fetch user status and assessment, if any
101
-            fetchUserStatus(thisFragment);
105
+                // Fetch user status and assessment, if any
106
+                fetchUserStatus();
102 107
 //            fetchAssessment();
103 108
 
104 109
             }
@@ -108,20 +113,20 @@ public class AssessmentsFragment extends Fragment {
108 113
 
109 114
     }
110 115
 
111
-    private void fetchUserStatus(View thisFragment) {
116
+    private void fetchUserStatus() {
112 117
 
113 118
         // Get text fields and initialize other stuff
114
-        final TextView statusTextView = thisFragment.findViewById(R.id.statusTextView);
115
-        final TextView nextSurveyTextView = thisFragment.findViewById(R.id.nextSurveyTextView);
116
-        final Button surveyButton = thisFragment.findViewById(R.id.surveyButton);
117
-        final Button refreshButton = thisFragment.findViewById(R.id.refreshButton);
119
+        final TextView statusTextView = this.thisFragment.findViewById(R.id.statusTextView);
120
+        final TextView nextSurveyTextView = this.thisFragment.findViewById(R.id.nextSurveyTextView);
121
+        final Button surveyButton = this.thisFragment.findViewById(R.id.surveyButton);
122
+        final Button refreshButton = this.thisFragment.findViewById(R.id.refreshButton);
118 123
 
119 124
         // Change UI to indicate loading is happening
120
-        statusTextView.setText(R.string.userStatusLoadingText);
125
+        statusTextView.setText(R.string.loadingText);
121 126
         nextSurveyTextView.setVisibility(View.GONE);
122 127
 
123 128
         // Define task
124
-        FetchUserStatus userStatusTask = (FetchUserStatus) new FetchUserStatus(new URLEventListener() {
129
+        FetchUserStatus userStatusTask = new FetchUserStatus(new URLEventListener() {
125 130
 
126 131
             @Override
127 132
             public void onSuccess(UserStatusModel userStatus) {
@@ -136,8 +141,6 @@ public class AssessmentsFragment extends Fragment {
136 141
                     refreshButton.setVisibility(View.GONE);
137 142
 
138 143
                     // Toggle "Answer" button visibility depending on survey availability
139
-                    // TODO: Decide whether to show the button regardless of anything,
140
-                    //  since this may be optional functionally, but important for UX
141 144
                     if(userStatus.surveyAvailable()) {
142 145
                         surveyButton.setVisibility(View.VISIBLE);
143 146
                     } else {
@@ -164,7 +167,7 @@ public class AssessmentsFragment extends Fragment {
164 167
         });
165 168
 
166 169
         // Start task
167
-        userStatusTask.execute(this.device_token);
170
+        userStatusTask.execute(this.DEVICE_TOKEN);
168 171
 
169 172
     }
170 173
 
@@ -178,21 +181,17 @@ public class AssessmentsFragment extends Fragment {
178 181
 
179 182
         // Fetch the assessment to be taken (if there is at least one available),
180 183
         // and launch the assessment (if the button is pressed)
181
-        FetchAssessment assessmentTask = (FetchAssessment) new FetchAssessment(new URLEventListener() {
184
+        FetchAssessment assessmentTask = new FetchAssessment(new URLEventListener() {
182 185
 
183 186
             @Override
184 187
             public void onSuccess(AssessmentModel assessment) {
185
-
186
-                // Stop progress dialog
187 188
                 progressDialog.dismiss();
188
-
189
-                // Launch survey or notify there are none available, accordingly
190 189
                 if(assessment.isEmpty()) {
190
+                    fetchUserStatus(); // update status to let user know if it already had expired by the time he clicked
191 191
                     Toast.makeText(getContext(), "There are no surveys at the moment!", Toast.LENGTH_LONG).show();
192 192
                 } else {
193 193
                     launchSurvey(assessment);
194 194
                 }
195
-
196 195
             }
197 196
 
198 197
             @Override
@@ -205,7 +204,7 @@ public class AssessmentsFragment extends Fragment {
205 204
         });
206 205
 
207 206
         // Start task
208
-        assessmentTask.execute(this.device_token);
207
+        assessmentTask.execute(this.DEVICE_TOKEN);
209 208
 
210 209
     }
211 210
 
@@ -218,96 +217,115 @@ public class AssessmentsFragment extends Fragment {
218 217
     }
219 218
 
220 219
     private void launchSurvey(AssessmentModel assessment) {
221
-        this.id_subquestionnair = assessment.getID();
222
-        List<Step> allSteps = this.prepareSurveySteps(assessment);
223
-        OrderedTask task = new OrderedTask(SAMPLE_SURVEY, allSteps);
220
+        this.ASSESSMENT_ID = assessment.getID();
221
+        List<Step> steps = this.prepareSurveySteps(assessment);
222
+        OrderedTask task = new OrderedTask(SAMPLE_SURVEY, steps);
224 223
         Intent intent = ViewTaskActivity.newIntent(getContext(), task);
225 224
         startActivityForResult(intent, REQUEST_SURVEY);
226 225
     }
227 226
 
228 227
     private void processSurveyResult(TaskResult result) {
228
+
229
+        // Save results to local database
229 230
         StorageAccess.getInstance().getAppDatabase().saveTaskResult(result);
230 231
 
232
+        // Load results
233
+        TaskResult taskResult = StorageAccess.getInstance()
234
+                .getAppDatabase()
235
+                .loadLatestTaskResult(SAMPLE_SURVEY);
236
+
237
+        // Prepare and send results
238
+        AnsweredAssessmentModel answeredAssessment = this.prepareAnsweredAssessment(taskResult);
239
+        this.sendSurveyInfo(answeredAssessment);
240
+
241
+    }
242
+
243
+    private void sendSurveyInfo(final AnsweredAssessmentModel answeredAssessment) {
244
+
231 245
         // Initiate progress dialog
232 246
         // TODO: find substitute for this deprecated dialog box
233 247
         final ProgressDialog progressDialog = ProgressDialog.show(getContext(),
234 248
                 "Sending Answers",
235 249
                 "This shouldn't take long");
236 250
 
237
-        try {
238
-            sendSurveyInfo();
239
-            progressDialog.dismiss();
240
-        } catch(Exception e) {
241
-            // TODO: prompt user to retry sending results
242
-            progressDialog.dismiss();
243
-            Toast.makeText(getContext(), "Couldn't send answers!", Toast.LENGTH_LONG).show();
244
-            Log.e("ERROR WHILE SENDING ANSWERS", e.toString());
245
-            e.printStackTrace();
246
-        }
247
-    }
248
-
249
-    private void sendSurveyInfo() {
250
-
251
-        // Load survey results
252
-        TaskResult taskResult = StorageAccess.getInstance()
253
-                .getAppDatabase()
254
-                .loadLatestTaskResult(SAMPLE_SURVEY);
251
+        // Send answers and prompt user if he wants to retry sending them if an error occurs
252
+        SendAnswers sendAnswersTask = new SendAnswers(new URLEventListener() {
255 253
 
256
-        // Serialize results into JSON
257
-        String resultsJSON = this.serializeSurveyResults(taskResult);
254
+           @Override public void onSuccess() {
255
+               progressDialog.dismiss();
256
+               Toast.makeText(getContext(), "Answers sent!", Toast.LENGTH_LONG).show();
257
+               fetchUserStatus();
258
+           }
258 259
 
259
-        // Send JSON
260
-        Log.d(TAG, "preparedJSON:" + resultsJSON);
261
-        new SendAnswersToServer().execute(resultsJSON);
260
+           @Override
261
+           public void onFailure(Exception e) {
262
+               Log.e("ERROR WHILE SENDING ANSWERS", e.toString());
263
+               e.printStackTrace();
264
+               progressDialog.dismiss();
265
+               promptRetrySend(answeredAssessment);
266
+           }
262 267
 
263
-    }
264
-
265
-    private String serializeSurveyResults(TaskResult taskResult) {
268
+        });
266 269
 
267
-        JSONObject surveyResults = new JSONObject();
268 270
 
269
-        try {
271
+        // Prepare results and start task
272
+       sendAnswersTask.execute(answeredAssessment);
273
+
274
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
275
+//        Handler handler = new Handler();
276
+//        handler.postDelayed(new Runnable() {
277
+//            public void run() {
278
+//                progressDialog.dismiss();
279
+//                if(true) { // Imitate success
280
+//                    Toast.makeText(getContext(), "Answers sent!", Toast.LENGTH_LONG).show();
281
+//                    fetchUserStatus();
282
+//                } else { // Imitate failure
283
+//                    promptRetrySend2(answeredAssessment);
284
+//                }
285
+//            }
286
+//        }, 5000);
287
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
270 288
 
271
-            surveyResults.put("os", "Android");
289
+    }
272 290
 
273
-            JSONObject data = new JSONObject();
274
-            data.put("id_subquestionnair", this.id_subquestionnair);
275
-            data.put("token", this.device_token);
291
+    private AnsweredAssessmentModel prepareAnsweredAssessment(TaskResult taskResult) {
276 292
 
277
-            // TODO: Try Gson.toJSON() to simplify this process
278
-            for (String id : taskResult.getResults().keySet()) {
279
-                JSONObject questions = new JSONObject();
280
-                StepResult stepResult = taskResult.getStepResult(id);
293
+        // Gather answered questions
294
+        ArrayList<AnsweredQuestionModel> answeredQuestions = new ArrayList<>();
295
+        for (String questionID : taskResult.getResults().keySet()) {
281 296
 
282
-                questions.accumulate("id_question", id);
283
-                questions.accumulate("start_datetime", taskResult.getStartDate().toString());
284
-                questions.accumulate("question_answer", stepResult.getResult().toString());
285
-                questions.accumulate("end_datetime", taskResult.getEndDate().toString());
286
-                data.accumulate("preguntas", questions.toString());
287
-            }
297
+            // Extract question info
298
+            // TODO: check if instead of getting the taskResult start/end times,
299
+            //  we should be getting the stepResult start/end times...
300
+            //  IF NOT: we should think about restructuring the JSON
301
+            //  because we're being highly redundant and inefficient
302
+            StepResult stepResult = taskResult.getStepResult(questionID);
303
+            String answer = stepResult.getResult().toString();
304
+            String startDatetime = taskResult.getStartDate().toString();
305
+            String endDatetime = taskResult.getEndDate().toString();
288 306
 
289
-            surveyResults.put("data", data.toString());
307
+            // Append question
308
+            AnsweredQuestionModel answeredQuestion = new AnsweredQuestionModel(questionID, answer, startDatetime, endDatetime);
309
+            answeredQuestions.add(answeredQuestion);
290 310
 
291
-        } catch (JSONException e) {
292
-            e.printStackTrace();
293 311
         }
294 312
 
295
-        return surveyResults.toString();
313
+        // Return answered assessment
314
+        return new AnsweredAssessmentModel(this.ASSESSMENT_ID, this.DEVICE_TOKEN, answeredQuestions);
296 315
 
297 316
     }
298 317
 
299 318
     private List<Step> prepareSurveySteps(AssessmentModel assessment) {
300 319
 
301 320
         // Initialize survey steps
302
-//        List<QuestionStep> taniaQuestions = new ArrayList<>();
303
-        List<Step> allSteps = new ArrayList<>();
321
+        List<Step> steps = new ArrayList<>();
304 322
 
305 323
         // Create instruction screen (add it to total steps)
306 324
         InstructionStep instructionStep = new InstructionStep(INSTRUCTION,
307 325
                 getString(R.string.app_name),
308 326
                 assessment.getDescription());
309
-        instructionStep.setStepTitle(R.string.survey);
310
-        allSteps.add(0, instructionStep);
327
+        instructionStep.setStepTitle(R.string.surveyToolbarText);
328
+        steps.add(0, instructionStep);
311 329
 
312 330
         // Prepare survey question by question
313 331
         List<QuestionModel> questions = assessment.getQuestions();
@@ -315,7 +333,7 @@ public class AssessmentsFragment extends Fragment {
315 333
         for(int i = 0; i < assessment.getQuestionCount(); i++) {
316 334
 
317 335
             QuestionModel question = questions.get(i);
318
-            Log.d("id_type", question.getType());
336
+            Log.d(TAG, "QuestionType:" + question.getType());
319 337
 
320 338
             if(question.getType().equals("SCALED")) {
321 339
 
@@ -336,35 +354,61 @@ public class AssessmentsFragment extends Fragment {
336 354
                 AnswerFormat multiFormat = new ChoiceAnswerFormat(AnswerFormat.ChoiceAnswerStyle.SingleChoice, choices);
337 355
 
338 356
                 // Set question step details
339
-                newQuestionStep.setStepTitle(R.string.survey);
357
+                newQuestionStep.setStepTitle(R.string.surveyToolbarText);
340 358
                 newQuestionStep.setTitle(question.getPremise());
341 359
                 newQuestionStep.setAnswerFormat(multiFormat);
342 360
                 newQuestionStep.setOptional(false);
343 361
 
344 362
                 // Add step to arrays
345
-//                taniaQuestions.add(i, newQuestionStep);
346
-                allSteps.add(newQuestionStep);
363
+                steps.add(newQuestionStep);
347 364
 
348 365
             } else if(question.getType().equals("OPEN")) {
349 366
 
350 367
                 // Create question step along with its choices
351 368
                 QuestionStep newQuestionStep = new QuestionStep(question.getID());
352
-                TextAnswerFormat format = new TextAnswerFormat();
369
+                TextAnswerFormat format = new TextAnswerFormat(100);
370
+                format.setIsMultipleLines(true);
353 371
 
354 372
                 // Set question step details
355
-                newQuestionStep.setStepTitle(R.string.survey);
373
+                newQuestionStep.setStepTitle(R.string.surveyToolbarText);
356 374
                 newQuestionStep.setTitle(question.getPremise());
357 375
                 newQuestionStep.setAnswerFormat(format);
358 376
                 newQuestionStep.setOptional(false);
359 377
 
360 378
                 // Add step to arrays
361
-//                taniaQuestions.add(i, newQuestionStep);
362
-                allSteps.add(newQuestionStep);
379
+                steps.add(newQuestionStep);
363 380
 
381
+            } else {
382
+                Log.wtf(TAG, "Neither OPEN nor SCALED if-statement has been reached");
364 383
             }
384
+
365 385
         }
366 386
 
367
-        return allSteps;
387
+        return steps;
388
+    }
389
+
390
+    private void promptRetrySend(final AnsweredAssessmentModel answeredAssessment) {
391
+        AlertDialog.Builder builder = new AlertDialog.Builder(Objects.requireNonNull(getContext()));
392
+        builder.setCancelable(true);
393
+        builder.setTitle("ERROR");
394
+        builder.setMessage("Couldn't send answer!");
395
+        builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
396
+            @Override
397
+            public void onClick(DialogInterface dialog, int which) {
398
+                Log.d(TAG, "Retrying answer send!");
399
+                sendSurveyInfo(answeredAssessment);
400
+            }
401
+        });
402
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
403
+            @Override
404
+            public void onClick(DialogInterface dialog, int which) {
405
+                Toast.makeText(getContext(), "Didn't send answers!", Toast.LENGTH_LONG).show();
406
+                fetchUserStatus();
407
+            }
408
+        });
409
+
410
+        AlertDialog dialog = builder.create();
411
+        dialog.show();
368 412
     }
369 413
 
370 414
 }

+ 12
- 17
app/src/main/java/uprrp/tania/fragments/ConsentFragment.java View File

@@ -1,8 +1,3 @@
1
-/*************************************************************
2
- * By: Coralys Cubero Rivera
3
- * Date: 2019
4
- *************************************************************/
5
-
6 1
 package uprrp.tania.fragments;
7 2
 
8 3
 import android.graphics.Canvas;
@@ -30,19 +25,19 @@ import java.io.File;
30 25
 
31 26
 import uprrp.tania.R;
32 27
 
33
-
34 28
 public class ConsentFragment extends Fragment {
35 29
 
36
-    PDFView pdfView;
30
+    private static final String TAG = "ConsentFragment";
31
+    private PDFView pdfView;
37 32
 
38 33
     @Nullable
39 34
     @Override
40 35
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
41 36
         View view = inflater.inflate(R.layout.fragment_consent, container, false);
42 37
 
43
-        pdfView = view.findViewById(R.id.pdf_viewer);
44
-        File consentFile = new File(Environment.getExternalStorageDirectory().getPath() + "/consent-tania-app.pdf");
45
-        pdfView.fromFile(consentFile)
38
+        this.pdfView = view.findViewById(R.id.pdf_viewer);
39
+        File consentFile = new File(Environment.getExternalStorageDirectory().getPath() + getString(R.string.consentFileName));
40
+        this.pdfView.fromFile(consentFile)
46 41
                 .password(null)
47 42
                 .defaultPage(0)
48 43
                 .enableSwipe(true)
@@ -77,12 +72,13 @@ public class ConsentFragment extends Fragment {
77 72
                     public boolean onTap(MotionEvent e) {
78 73
                         return true;
79 74
                     }
80
-                }).onRender(new OnRenderListener() {
81
-            @Override
82
-            public void onInitiallyRendered(int nbPages, float pageWidth, float pageHeight) {
83
-                pdfView.fitToWidth();
84
-            }
85
-        })
75
+                })
76
+                .onRender(new OnRenderListener() {
77
+                    @Override
78
+                    public void onInitiallyRendered(int nbPages, float pageWidth, float pageHeight) {
79
+                        pdfView.fitToWidth();
80
+                    }
81
+                })
86 82
                 .enableAnnotationRendering(true)
87 83
                 .invalidPageColor(Color.WHITE)
88 84
                 .load();
@@ -90,5 +86,4 @@ public class ConsentFragment extends Fragment {
90 86
         return view;
91 87
     }
92 88
 
93
-
94 89
 }

+ 111
- 44
app/src/main/java/uprrp/tania/fragments/WithdrawFragment.java View File

@@ -1,16 +1,17 @@
1
-/*************************************************************
2
- * By: Coralys Cubero Rivera
3
- * Date: 2019
4
- *************************************************************/
5
-
6 1
 package uprrp.tania.fragments;
7 2
 
3
+import android.app.ProgressDialog;
4
+import android.content.Context;
8 5
 import android.content.DialogInterface;
6
+import android.content.Intent;
7
+import android.content.SharedPreferences;
9 8
 import android.os.Bundle;
9
+import android.util.Log;
10 10
 import android.view.LayoutInflater;
11 11
 import android.view.View;
12 12
 import android.view.ViewGroup;
13 13
 import android.widget.Button;
14
+import android.widget.Toast;
14 15
 
15 16
 import androidx.annotation.NonNull;
16 17
 import androidx.annotation.Nullable;
@@ -21,67 +22,133 @@ import com.google.android.gms.tasks.OnSuccessListener;
21 22
 import com.google.firebase.iid.FirebaseInstanceId;
22 23
 import com.google.firebase.iid.InstanceIdResult;
23 24
 
24
-import org.json.JSONException;
25
-import org.json.JSONObject;
26
-
27 25
 import java.util.Objects;
28 26
 
29 27
 import uprrp.tania.R;
30
-import uprrp.tania.SendWithdrawalToServer;
28
+import uprrp.tania.URLEventListener;
29
+import uprrp.tania.activities.GettingStartedActivity;
30
+import uprrp.tania.networking.SendWithdrawal;
31 31
 
32 32
 public class WithdrawFragment extends Fragment {
33 33
 
34
-    private String device_token;
34
+    private static final String TAG = "WithdrawFragment";
35
+    private String DEVICE_TOKEN;
35 36
 
36 37
     @Nullable
37 38
     @Override
38 39
     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
40
+
41
+        // Get reference for this fragment and other components
39 42
         View view = inflater.inflate(R.layout.fragment_withdrawal, container, false);
43
+        final Button withdrawButton = view.findViewById(R.id.withdrawButton);
40 44
 
41
-        //Let's get the device's token
45
+        // Change all caps text to normal capitalization
46
+        // TODO: this is a workaround I found, any other acceptable solution is welcome
47
+        withdrawButton.setTransformationMethod(null);
48
+        withdrawButton.setText(R.string.loadingText);
49
+
50
+        // Get device's token and attach click listener once we have it
42 51
         FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
43 52
             @Override
44 53
             public void onSuccess(InstanceIdResult instanceIdResult) {
45
-                device_token = instanceIdResult.getToken();
54
+                DEVICE_TOKEN = instanceIdResult.getToken();
55
+                withdrawButton.setOnClickListener(new View.OnClickListener() {
56
+                    @Override
57
+                    public void onClick(View v) {
58
+                        promptConfirmation();
59
+                    }
60
+                });
61
+                withdrawButton.setText(R.string.withdrawButtonText);
46 62
             }
47 63
         });
48 64
 
49
-        Button withdrawButton = view.findViewById(R.id.withdrawButton);
50
-        withdrawButton.setTransformationMethod(null); // change all caps to normal
51
-        withdrawButton.setOnClickListener(new View.OnClickListener() {
65
+        return view;
66
+    }
67
+
68
+    private void promptConfirmation() {
69
+        AlertDialog.Builder builder = new AlertDialog.Builder(Objects.requireNonNull(getContext()));
70
+        builder.setCancelable(true);
71
+        builder.setTitle("WITHDRAWING");
72
+        builder.setMessage("Are you absolutely sure you want to withdraw from the project?");
73
+        builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
52 74
             @Override
53
-            public void onClick(View v) {
54
-                AlertDialog.Builder builder = new AlertDialog.Builder(Objects.requireNonNull(getContext()));
55
-                builder.setCancelable(true);
56
-                builder.setTitle("WITHDRAWING");
57
-                builder.setMessage("Are you absolutely sure you want to withdraw from the project?");
58
-                builder.setPositiveButton("Confirm",
59
-                        new DialogInterface.OnClickListener() {
60
-                            @Override
61
-                            public void onClick(DialogInterface dialog, int which) {
62
-                                try {
63
-                                    JSONObject withdrawToken = new JSONObject();
64
-                                    withdrawToken.put("token", device_token);
65
-                                    SendWithdrawalToServer sendWithdrawalToServer = new SendWithdrawalToServer(getContext());
66
-                                    sendWithdrawalToServer.execute(withdrawToken.toString());
67
-                                }
68
-                                catch (JSONException e){
69
-                                    e.getMessage();
70
-                                }
71
-
72
-                            }
73
-                        });
74
-                builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
75
-                    @Override
76
-                    public void onClick(DialogInterface dialog, int which) {
77
-                    }
78
-                });
75
+            public void onClick(DialogInterface dialog, int which) {
76
+                sendWithdrawalRequest();
77
+            }
78
+        });
79
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
80
+            @Override
81
+            public void onClick(DialogInterface dialog, int which) {
82
+                Log.d(TAG, "Cancelled withdrawal!");
83
+            }
84
+        });
85
+
86
+        AlertDialog dialog = builder.create();
87
+        dialog.show();
88
+    }
89
+
90
+    private void sendWithdrawalRequest() {
91
+
92
+        // Initiate progress dialog
93
+        // TODO: find substitute for this deprecated dialog box
94
+        final ProgressDialog progressDialog = ProgressDialog.show(getContext(),
95
+                "Withdrawing",
96
+                "This shouldn't take long");
97
+
98
+        // Send withdrawal request to server
99
+        SendWithdrawal sendWithdrawalTask = new SendWithdrawal(new URLEventListener() {
100
+            @Override
101
+            public void onSuccess() {
102
+
103
+                progressDialog.dismiss();
104
+                Context context = getContext();
105
+
106
+                // Change system preferences to begin in GettingStartedActivity by default
107
+                SharedPreferences prefs = context.getSharedPreferences("prefs", Context.MODE_PRIVATE);
108
+                SharedPreferences.Editor editor = prefs.edit();
109
+                editor.putBoolean("needsToRegister", true);
110
+                editor.apply();
111
+
112
+                // Start GettingStartedActivity with a success message
113
+                Intent intent = new Intent(context, GettingStartedActivity.class);
114
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
115
+                context.startActivity(intent);
116
+                Toast.makeText(context.getApplicationContext(), "Successfully withdrawn!", Toast.LENGTH_LONG).show();
79 117
 
80
-                AlertDialog dialog = builder.create();
81
-                dialog.show();
118
+            }
119
+
120
+            @Override
121
+            public void onFailure(Exception e) {
122
+                progressDialog.dismiss();
123
+                Toast.makeText(getContext(), "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
124
+                Log.e(TAG, "Error occurred while sending withdrawal request to server...");
125
+                e.printStackTrace();
82 126
             }
83 127
         });
84 128
 
85
-        return view;
129
+        // Start task
130
+        sendWithdrawalTask.execute(DEVICE_TOKEN);
131
+
132
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
133
+        /*
134
+        Handler handler = new Handler();
135
+        handler.postDelayed(new Runnable() {
136
+            public void run() {
137
+                progressDialog.dismiss();
138
+                if(true) { // Imitate success
139
+                    Context context = getContext();
140
+                    Intent intent = new Intent(context, GettingStartedActivity.class);
141
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
142
+                    context.startActivity(intent);
143
+                    Toast.makeText(context.getApplicationContext(), "Successfully withdrawn!", Toast.LENGTH_LONG).show();
144
+                } else { // Imitate failure
145
+                    Toast.makeText(getContext(), "Oops! Something went wrong...", Toast.LENGTH_LONG).show();
146
+                }
147
+            }
148
+        }, 5000);
149
+        */
150
+        // UNCOMMENT THIS FOR TESTING (REMEMBER TO COMMENT .execute() LINE)
151
+
86 152
     }
153
+
87 154
 }

+ 16
- 0
app/src/main/java/uprrp/tania/models/AnsweredAssessmentModel.java View File

@@ -0,0 +1,16 @@
1
+package uprrp.tania.models;
2
+
3
+import java.util.ArrayList;
4
+
5
+public class AnsweredAssessmentModel {
6
+    // NOTE these attribute must have the same names as the JSON fields we'll be sending to the server
7
+    private final String id_subquestionnair;
8
+    private final String token;
9
+    private final ArrayList<AnsweredQuestionModel> preguntas;
10
+
11
+    public AnsweredAssessmentModel(String id, String token, ArrayList<AnsweredQuestionModel> answeredQuestions) {
12
+        this.id_subquestionnair = id;
13
+        this.token = token;
14
+        this.preguntas = answeredQuestions;
15
+    }
16
+}

+ 16
- 0
app/src/main/java/uprrp/tania/models/AnsweredQuestionModel.java View File

@@ -0,0 +1,16 @@
1
+package uprrp.tania.models;
2
+
3
+public class AnsweredQuestionModel {
4
+    // NOTE these attribute must have the same names as the JSON fields we'll be sending to the server
5
+    private final String id_question;
6
+    private final String question_answer;
7
+    private final String start_datetime;
8
+    private final String end_datetime;
9
+
10
+    public AnsweredQuestionModel(String id, String answer, String startDatetime, String endDatetime) {
11
+        this.id_question = id;
12
+        this.question_answer = answer;
13
+        this.start_datetime = startDatetime;
14
+        this.end_datetime = endDatetime;
15
+    }
16
+}

+ 2
- 2
app/src/main/java/uprrp/tania/models/QuestionModel.java View File

@@ -10,7 +10,7 @@ public class QuestionModel {
10 10
     private String premise;
11 11
     private String max_text;
12 12
     private String min_text;
13
-    private int max_value;
13
+    private int max_val;
14 14
 
15 15
     public String getID() {
16 16
         return this.id_question;
@@ -40,7 +40,7 @@ public class QuestionModel {
40 40
     }
41 41
 
42 42
     public int getMaxValue() {
43
-        return this.max_value;
43
+        return this.max_val;
44 44
     }
45 45
 
46 46
     public int getMinValue() {

+ 7
- 0
app/src/main/java/uprrp/tania/models/UserStatusModel.java View File

@@ -41,6 +41,13 @@ public class UserStatusModel {
41 41
 
42 42
         // Format is DD:HH:MM (can be negative if survey can be answered)
43 43
         String[] parts = this.time_to_next.split(":");
44
+
45
+        // Input validation
46
+        if(parts[0].equals("") || parts[1].equals("") || parts[2].equals("")) {
47
+            Log.wtf(TAG, "Backend gave an invalid time_to_next: " + this.time_to_next);
48
+            return ".."; // forms ellipsis
49
+        }
50
+
44 51
         int days = Integer.parseInt(parts[0]);
45 52
         int hours = Integer.parseInt(parts[1]);
46 53
         int minutes = Integer.parseInt(parts[2]);

+ 17
- 9
app/src/main/java/uprrp/tania/networking/FetchAssessment.java View File

@@ -1,5 +1,6 @@
1 1
 package uprrp.tania.networking;
2 2
 
3
+import android.content.res.Resources;
3 4
 import android.os.AsyncTask;
4 5
 import android.util.Log;
5 6
 
@@ -18,14 +19,14 @@ import java.net.URL;
18 19
 
19 20
 import javax.net.ssl.HttpsURLConnection;
20 21
 
22
+import uprrp.tania.R;
21 23
 import uprrp.tania.URLEventListener;
22 24
 import uprrp.tania.models.AssessmentModel;
23 25
 import uprrp.tania.models.EmptyAssessmentModel;
24 26
 
25 27
 public class FetchAssessment extends AsyncTask<String, Void, JSONObject> {
26 28
 
27
-    private final String TAG = "FETCH ASSESSMENT";
28
-    private final StringBuilder data =  new StringBuilder();
29
+    private static final String TAG = "FetchAssessment";
29 30
     private final URLEventListener myCallBack;
30 31
 
31 32
     public FetchAssessment(URLEventListener callback) {
@@ -38,36 +39,43 @@ public class FetchAssessment extends AsyncTask<String, Void, JSONObject> {
38 39
 
39 40
         try {
40 41
 
41
-            String getMomentsBaseURL = "https://tania.uprrp.edu/getSubQ2.php?tk="; // TODO: extract URL into @string/getMomentsBaseURL
42
+            String getMomentsBaseURL = Resources.getSystem().getString(R.string.assessmentsBaseURL);
42 43
             URL url = new URL(getMomentsBaseURL + deviceToken);
43 44
             Log.d(TAG, "token:" + deviceToken); // log
44 45
             Log.d(TAG, "url:" + url.toString()); // log
45 46
             HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
46 47
             InputStream inputStream = httpsURLConnection.getInputStream();
47 48
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
49
+            StringBuilder serverResponse =  new StringBuilder();
48 50
 
49 51
             String line = "";
50 52
             while (line != null) {
51
-                data.append(line);
53
+                serverResponse.append(line);
52 54
                 line = bufferedReader.readLine();
53 55
             }
54 56
 
55
-            Log.d(TAG, data.toString().substring(0, 10)); // log
56
-            if(data.toString().startsWith("Error:NoHa")) {
57
+            String response = serverResponse.toString();
58
+            Log.d(TAG, response.substring(0, 10)); // log
59
+            if(response.startsWith("Error:NoHa")) {
57 60
                 return new JSONObject("{}");
58 61
             } else {
59
-                return new JSONObject(data.toString());
62
+                return new JSONObject(response);
60 63
             }
61 64
 
62 65
         } catch (MalformedURLException e) {
66
+            Log.e(TAG, "Wrong URL used!");
63 67
             e.printStackTrace();
68
+            return null;
64 69
         } catch (IOException e) {
70
+            Log.e(TAG, "IOStream error occurred!");
65 71
             e.printStackTrace();
72
+            return null;
66 73
         } catch (JSONException e) {
74
+            Log.e(TAG, "Couldn't construct return JSON!");
67 75
             e.printStackTrace();
76
+            return null;
68 77
         }
69 78
 
70
-        return null;
71 79
     }
72 80
 
73 81
     @Override
@@ -79,7 +87,7 @@ public class FetchAssessment extends AsyncTask<String, Void, JSONObject> {
79 87
         }
80 88
 
81 89
         if(obj == null) {
82
-            this.myCallBack.onFailure(new Exception("An error occurred during FetchAssesment!"));
90
+            this.myCallBack.onFailure(new Exception("An error occurred during FetchAssessment!"));
83 91
             return;
84 92
         }
85 93
 

+ 21
- 11
app/src/main/java/uprrp/tania/networking/FetchUserStatus.java View File

@@ -1,31 +1,33 @@
1 1
 package uprrp.tania.networking;
2 2
 
3
+import android.content.res.Resources;
3 4
 import android.os.AsyncTask;
4 5
 import android.util.Log;
5 6
 
6 7
 import com.google.gson.GsonBuilder;
7 8
 import com.google.gson.JsonSyntaxException;
8 9
 
9
-import org.json.JSONException;
10 10
 import org.json.JSONArray;
11
+import org.json.JSONException;
11 12
 import org.json.JSONObject;
12 13
 
13 14
 import java.io.BufferedReader;
14 15
 import java.io.IOException;
15 16
 import java.io.InputStream;
16 17
 import java.io.InputStreamReader;
17
-import java.net.HttpURLConnection;
18 18
 import java.net.MalformedURLException;
19 19
 import java.net.URL;
20 20
 
21
+import javax.net.ssl.HttpsURLConnection;
22
+
23
+import uprrp.tania.R;
21 24
 import uprrp.tania.URLEventListener;
22 25
 import uprrp.tania.models.EmptyUserStatusModel;
23 26
 import uprrp.tania.models.UserStatusModel;
24 27
 
25 28
 public class FetchUserStatus extends AsyncTask<String, Void, JSONArray> {
26 29
 
27
-    private final String TAG = "FETCH USER STATUS";
28
-    private final StringBuilder data =  new StringBuilder();
30
+    private static final String TAG = "FetchUserStatus";
29 31
     private final URLEventListener myCallBack;
30 32
 
31 33
     public FetchUserStatus(URLEventListener callback) {
@@ -37,36 +39,44 @@ public class FetchUserStatus extends AsyncTask<String, Void, JSONArray> {
37 39
         String deviceToken = deviceTokenArray[0]; // array will only ever contain a single element
38 40
 
39 41
         try {
40
-            String userStatusBaseURL = "https://tania.uprrp.edu/status.php?tk=";  // TODO: extract URL into @string/userStatusBaseURL
42
+
43
+            String userStatusBaseURL = Resources.getSystem().getString(R.string.userStatusBaseURL);
41 44
             URL url = new URL(userStatusBaseURL + deviceToken);
42 45
             Log.d(TAG, "token:" + deviceToken); // log
43 46
             Log.d(TAG, "url:" + url.toString()); // log
44
-            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
47
+            HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection();
45 48
             InputStream inputStream = httpURLConnection.getInputStream();
46 49
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
50
+            StringBuilder serverResponse =  new StringBuilder();
47 51
 
48 52
             String line = "";
49 53
             while(line != null) {
50
-                data.append(line);
54
+                serverResponse.append(line);
51 55
                 line = bufferedReader.readLine();
52 56
             }
53 57
 
54
-            Log.d(TAG, "rawString:" + data.toString()); // log
55
-            if(data.toString().equals("no esta inscrito")) {
58
+            String response = serverResponse.toString();
59
+            Log.d(TAG, "rawString:" + response); // log
60
+            if(response.equals("no esta inscrito")) {
56 61
                 return new JSONArray("[]");
57 62
             } else {
58
-                return new JSONArray(data.toString());
63
+                return new JSONArray(response);
59 64
             }
60 65
 
61 66
         } catch (MalformedURLException e) {
67
+            Log.e(TAG, "Wrong URL used!");
62 68
             e.printStackTrace();
69
+            return null;
63 70
         } catch (IOException e) {
71
+            Log.e(TAG, "IOStream error occurred!");
64 72
             e.printStackTrace();
73
+            return null;
65 74
         } catch (JSONException e) {
75
+            Log.e(TAG, "Couldn't construct return JSON!");
66 76
             e.printStackTrace();
77
+            return null;
67 78
         }
68 79
 
69
-        return null;
70 80
     }
71 81
 
72 82
     @Override

+ 163
- 0
app/src/main/java/uprrp/tania/networking/SendAnswers.java View File

@@ -0,0 +1,163 @@
1
+package uprrp.tania.networking;
2
+
3
+import android.content.res.Resources;
4
+import android.os.AsyncTask;
5
+import android.util.Log;
6
+
7
+import com.google.gson.GsonBuilder;
8
+import com.google.gson.JsonSyntaxException;
9
+
10
+import org.json.JSONException;
11
+import org.json.JSONObject;
12
+
13
+import java.io.BufferedReader;
14
+import java.io.InputStreamReader;
15
+import java.io.OutputStreamWriter;
16
+import java.io.UnsupportedEncodingException;
17
+import java.net.URL;
18
+import java.net.URLEncoder;
19
+
20
+import javax.net.ssl.HttpsURLConnection;
21
+
22
+import uprrp.tania.R;
23
+import uprrp.tania.URLEventListener;
24
+import uprrp.tania.models.AnsweredAssessmentModel;
25
+
26
+public class SendAnswers extends AsyncTask<AnsweredAssessmentModel, Void, String> {
27
+
28
+    private static final String TAG = "SendAnswers";
29
+    private final URLEventListener myCallback;
30
+
31
+    public SendAnswers(URLEventListener callback) {
32
+        this.myCallback = callback;
33
+    }
34
+
35
+    // TODO: instead of making a normal POST request with
36
+    //  the body data=<uglyJSON>, we should just send the JSON itself
37
+    //  This is adding unnecessary layers of complexity when decoding AND encoding
38
+    /*
39
+        uglyJSON Format:
40
+        {
41
+            "os": "Android",
42
+            "data": String (containing escaped serializedAssessmentJSON)
43
+        }
44
+
45
+        serializedAssessmentJSON Format:
46
+        {
47
+            "id_subquestionnair": String,
48
+            "token": String,
49
+            "preguntas": [
50
+                {
51
+                    "id_question": String,
52
+                    "start_datetime": String,
53
+                    "question_answer": String,
54
+                    "end_datetime": String
55
+                },
56
+                ...
57
+            ]
58
+        }
59
+
60
+        WE SHOULD BE INSTEAD SENDING SOMETHING LIKE:
61
+        {
62
+            "os": "Android",
63
+            "token" String,
64
+            "data": {
65
+                "id_subquestionnair": String,
66
+                "preguntas": [
67
+                    {
68
+                        "id_question": String,
69
+                        "start_datetime": String,
70
+                        "question_answer": String,
71
+                        "end_datetime": String
72
+                    },
73
+                    ...
74
+                ]
75
+            }
76
+        }
77
+     */
78
+    @Override
79
+    protected String doInBackground(AnsweredAssessmentModel... answeredAssessments) {
80
+
81
+        AnsweredAssessmentModel answeredAssessment = answeredAssessments[0]; // array will only ever contain a single element
82
+
83
+        try {
84
+
85
+            // Serialize answered assessment
86
+            GsonBuilder builder = new GsonBuilder();
87
+//            builder.setPrettyPrinting(); // the server doesn't like pretty printing...
88
+            String serializedAssessment = builder.create().toJson(answeredAssessment);
89
+            Log.d(TAG,"reconstructedJSON:\n" + serializedAssessment); // log
90
+
91
+            // Create wrapper json (a.k.a. uglyJSON)
92
+            JSONObject surveyResults = new JSONObject();
93
+            surveyResults.put("os", "Android");
94
+            surveyResults.put("data", serializedAssessment);
95
+
96
+            // Encode data
97
+            String encodedRequestBody = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(surveyResults.toString(), "UTF-8");
98
+
99
+            // Send POST data request
100
+            URL url = new URL(Resources.getSystem().getString(R.string.sendAnswersURL));
101
+            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
102
+            conn.setDoOutput(true);
103
+            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
104
+            wr.write(encodedRequestBody);
105
+            wr.flush();
106
+
107
+            // Get the server response
108
+            BufferedReader serverReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
109
+            StringBuilder serverResponse = new StringBuilder();
110
+
111
+            String line = "";
112
+            while(line != null) {
113
+                serverResponse.append(line);
114
+                line = serverReader.readLine();
115
+            }
116
+
117
+            String response = serverResponse.toString();
118
+            Log.d(TAG, "The server's response is: " + response);
119
+            if(response.startsWith("Success")) {
120
+                return response;
121
+            } else {
122
+                return null;
123
+            }
124
+
125
+
126
+        } catch (JsonSyntaxException e) {
127
+            Log.e(TAG, "Failed to JSONify AnsweredAssessment!");
128
+            e.printStackTrace();
129
+            return null;
130
+        } catch (JSONException e) {
131
+            Log.e(TAG, "Failed to put AnsweredAssessment to surveyResults!");
132
+            e.printStackTrace();
133
+            return null;
134
+        } catch (UnsupportedEncodingException e) {
135
+            Log.e(TAG, "Couldn't encode surveyResultsJSON!");
136
+            e.printStackTrace();
137
+            return null;
138
+        } catch(Exception e) {
139
+            Log.e(TAG,"Couldn't communicate with server while sending answers!" + e.getMessage());
140
+            e.printStackTrace();
141
+            return null;
142
+        }
143
+
144
+    }
145
+
146
+    @Override
147
+    protected void onPostExecute(String response) {
148
+
149
+        if(this.myCallback == null) {
150
+            this.myCallback.onFailure(new Exception("Callback wasn't initialized first!"));
151
+            return;
152
+        }
153
+
154
+        if(response == null) {
155
+            this.myCallback.onFailure(new Exception("Error occurred during transaction!"));
156
+            return;
157
+        }
158
+
159
+        this.myCallback.onSuccess();
160
+
161
+    }
162
+
163
+}

+ 126
- 0
app/src/main/java/uprrp/tania/networking/SendExperienceRegistration.java View File

@@ -0,0 +1,126 @@
1
+package uprrp.tania.networking;
2
+
3
+import android.content.res.Resources;
4
+import android.os.AsyncTask;
5
+import android.util.Log;
6
+
7
+import org.json.JSONException;
8
+import org.json.JSONObject;
9
+
10
+import java.io.BufferedReader;
11
+import java.io.InputStreamReader;
12
+import java.io.OutputStreamWriter;
13
+import java.io.UnsupportedEncodingException;
14
+import java.net.URL;
15
+import java.net.URLEncoder;
16
+
17
+import javax.net.ssl.HttpsURLConnection;
18
+
19
+import uprrp.tania.R;
20
+import uprrp.tania.URLEventListener;
21
+
22
+public class SendExperienceRegistration extends AsyncTask<String, String, String> {
23
+
24
+    private static final String TAG = "SendActivateExperience";
25
+    private final URLEventListener myCallback;
26
+
27
+    public SendExperienceRegistration(URLEventListener callback) {
28
+        this.myCallback = callback;
29
+    }
30
+
31
+    // TODO: instead of making a normal POST request with
32
+    //  the body data=<JSON>, we should just send the JSON itself
33
+    //  This is adding unnecessary layers of complexity when decoding AND encoding
34
+    /*
35
+        JSON format:
36
+        {
37
+            "token": String,
38
+            "id_experiencia": String
39
+        }
40
+    */
41
+    @Override
42
+    protected String doInBackground(String... strings) {
43
+
44
+        String deviceToken = strings[0];
45
+        String experienceID = strings[1];
46
+
47
+        if(experienceID == null) {
48
+            Log.e(TAG, "Encountered null experience ID!");
49
+            return null;
50
+        } else if (deviceToken == null) {
51
+            Log.e(TAG, "Encountered null device token!");
52
+            return null;
53
+        }
54
+
55
+        try {
56
+
57
+            // Create JSON
58
+            JSONObject experienceRegistrationJSON = new JSONObject();
59
+            experienceRegistrationJSON.put("token", deviceToken);
60
+            experienceRegistrationJSON.put("id_experiencia", experienceID);
61
+            Log.d(TAG, "Prepared JSON: " + experienceRegistrationJSON.toString());
62
+
63
+            // Encode data
64
+            String encodedRequestBody = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(experienceRegistrationJSON.toString(), "UTF-8");
65
+
66
+            // Send POST data request
67
+            URL url = new URL(Resources.getSystem().getString(R.string.activateExperienceURL));
68
+            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
69
+            conn.setDoOutput(true);
70
+            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
71
+            wr.write(encodedRequestBody);
72
+            wr.flush();
73
+
74
+            // Get the server response
75
+            BufferedReader serverReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
76
+            StringBuilder serverResponse = new StringBuilder();
77
+
78
+            String line = "";
79
+            while(line != null) {
80
+                serverResponse.append(line);
81
+                line = serverReader.readLine();
82
+            }
83
+
84
+            // TODO: restrict double registration (maybe in backend?)
85
+            String response = serverResponse.toString();
86
+            Log.d(TAG, "The server's response is: " + response);
87
+            if(response.startsWith("Success")) {
88
+                return response;
89
+            } else {
90
+                return null;
91
+            }
92
+
93
+        } catch (JSONException e) {
94
+            Log.e(TAG, "Couldn't prepare experienceRegistrationJSON!");
95
+            e.printStackTrace();
96
+            return null;
97
+        } catch (UnsupportedEncodingException e) {
98
+            Log.e(TAG, "Couldn't encode experienceRegistrationJSON!");
99
+            e.printStackTrace();
100
+            return null;
101
+        } catch(Exception e) {
102
+            Log.e(TAG, "Couldn't communicate with server while activating experience!");
103
+            e.printStackTrace();
104
+            return null;
105
+        }
106
+
107
+    }
108
+
109
+    @Override
110
+    protected void onPostExecute(String response) {
111
+
112
+        if(this.myCallback == null) {
113
+            this.myCallback.onFailure(new Exception("Callback wasn't initialized first!"));
114
+            return;
115
+        }
116
+
117
+        if(response == null) {
118
+            this.myCallback.onFailure(new Exception("Error occurred during transaction!"));
119
+            return;
120
+        }
121
+
122
+        this.myCallback.onSuccess();
123
+
124
+    }
125
+
126
+}

+ 116
- 0
app/src/main/java/uprrp/tania/networking/SendWithdrawal.java View File

@@ -0,0 +1,116 @@
1
+package uprrp.tania.networking;
2
+
3
+import android.content.res.Resources;
4
+import android.os.AsyncTask;
5
+import android.util.Log;
6
+
7
+import org.json.JSONException;
8
+import org.json.JSONObject;
9
+
10
+import java.io.BufferedReader;
11
+import java.io.BufferedWriter;
12
+import java.io.InputStreamReader;
13
+import java.io.OutputStreamWriter;
14
+import java.io.UnsupportedEncodingException;
15
+import java.io.Writer;
16
+import java.net.URL;
17
+import java.net.URLEncoder;
18
+import java.nio.charset.StandardCharsets;
19
+
20
+import javax.net.ssl.HttpsURLConnection;
21
+
22
+import uprrp.tania.R;
23
+import uprrp.tania.URLEventListener;
24
+
25
+public class SendWithdrawal extends AsyncTask<String, String, String> {
26
+
27
+    private static final String TAG = "SendWithdrawal";
28
+    private final URLEventListener myCallback;
29
+
30
+    public SendWithdrawal(URLEventListener callback) {
31
+        this.myCallback = callback;
32
+    }
33
+
34
+    // TODO: instead of making a normal POST request with
35
+    //  the body data=<JSON>, we should just send the JSON itself
36
+    //  This is adding unnecessary layers of complexity when decoding AND encoding
37
+    /*
38
+        JSON format:
39
+        {
40
+            "token": String
41
+        }
42
+    */
43
+    @Override
44
+    protected String doInBackground(String... deviceTokenArray) {
45
+
46
+        String deviceToken = deviceTokenArray[0]; // array will only ever contain one element
47
+
48
+        try {
49
+
50
+            // Create JSON
51
+            JSONObject withdrawJSON = new JSONObject();
52
+            withdrawJSON.put("token", deviceToken);
53
+            Log.d(TAG, "Prepared JSON: " + withdrawJSON.toString());
54
+
55
+            // Encode data
56
+            String encodedRequestBody = URLEncoder.encode("data", "UTF-8") + "=" + URLEncoder.encode(withdrawJSON.toString(), "UTF-8");
57
+
58
+            // Send POST data request
59
+            URL url = new URL(Resources.getSystem().getString(R.string.withdrawalURL));
60
+            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
61
+            conn.setDoOutput(true);
62
+            Writer writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
63
+            writer.write(encodedRequestBody);
64
+            writer.close();
65
+
66
+            // Get the server response
67
+            BufferedReader serverReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
68
+            StringBuilder serverResponse = new StringBuilder();
69
+
70
+            String line = "";
71
+            while(line != null) {
72
+                serverResponse.append(line);
73
+                line = serverReader.readLine();
74
+            }
75
+
76
+            String response = serverResponse.toString();
77
+            Log.d(TAG, "The server's response is: " + response);
78
+            if(response.startsWith("Success")) {
79
+                return response;
80
+            } else {
81
+                return null;
82
+            }
83
+
84
+        } catch (JSONException e){
85
+            Log.e(TAG, "Couldn't construct withdrawJSON!");
86
+            e.printStackTrace();
87
+            return null;
88
+        } catch (UnsupportedEncodingException e) {
89
+            Log.e(TAG, "Couldn't encode withdrawJSON!");
90
+            e.printStackTrace();
91
+            return null;
92
+        } catch (Exception e) {
93
+            Log.e(TAG, "Couldn't communicate with server during withdrawal!");
94
+            e.printStackTrace();
95
+            return null;
96
+        }
97
+
98
+    }
99
+
100
+    @Override
101
+    protected void onPostExecute(String response) {
102
+
103
+        if(this.myCallback == null) {
104
+            this.myCallback.onFailure(new Exception("Callback wasn't initialized first!"));
105
+            return;
106
+        }
107
+
108
+        if(response == null) {
109
+            this.myCallback.onFailure(new Exception("Error occurred during transaction!"));
110
+            return;
111
+        }
112
+
113
+        this.myCallback.onSuccess();
114
+
115
+    }
116
+}

+ 1
- 1
app/src/main/res/layout/fragment_assessments.xml View File

@@ -59,7 +59,7 @@
59 59
         android:layout_marginStart="@dimen/horizontalMargin"
60 60
         android:layout_marginTop="16dp"
61 61
         android:layout_marginEnd="@dimen/horizontalMargin"
62
-        android:text="@string/userStatusLoadingText"
62
+        android:text="@string/loadingText"
63 63
         android:textSize="@dimen/sectionDescriptionTextSize"
64 64
         app:layout_constraintEnd_toEndOf="parent"
65 65
         app:layout_constraintStart_toStartOf="parent"

+ 12
- 2
app/src/main/res/values/strings.xml View File

@@ -1,6 +1,5 @@
1 1
 <resources>
2 2
     <string name="app_name">TANIA</string>
3
-    <string name="survey">TANIA ASSESSMENT</string>
4 3
     <string name="menu_data_cleared">La información ha sido borrada</string>
5 4
     <string name="user_name">Nombre de usuario:</string>
6 5
     <string name="date_consented">Fecha de consentimiento:</string>
@@ -24,9 +23,19 @@
24 23
 
25 24
     <!-- !!!!! POST-Víctor !!!!! -->
26 25
 
26
+    <!--  AmazonCognito Details  -->
27
+    <string name="identityPoolID">us-east-1:574094cd-0784-4e26-bd14-4fa72ae63579</string>
28
+    <!--  API Endpoints  -->
29
+    <string name="sendAnswersURL">https://tania.uprrp.edu/parseAnswers.php</string>
30
+    <string name="activateExperienceURL">https://tania.uprrp.edu/inscripcionExperiencia.php</string>
31
+    <string name="withdrawalURL">https://tania.uprrp.edu/withdrawal.php</string>
32
+    <string name="userStatusBaseURL">https://tania.uprrp.edu/status.php?tk=</string>
33
+    <string name="assessmentsBaseURL">https://tania.uprrp.edu/getSubQ2.php?tk=</string>
34
+    <!--  General App Configurations  -->
35
+    <string name="surveyToolbarText">Assessment</string>
27 36
     <!--  Assessment Fragment  -->
28 37
     <string name="mainGreetingText">Hello! 👋</string>
29
-    <string name="userStatusLoadingText">Please wait a moment…</string>
38
+    <string name="loadingText">Please wait a moment…</string>
30 39
     <string name="userStatusErrorText">It seems we can\'t identify you… Please try again later.</string>
31 40
     <string name="refreshButtonText">Refresh Status</string>
32 41
     <string name="surveyButtonText">Answer Survey</string>
@@ -38,4 +47,5 @@
38 47
     <string name="experienceRegistrationTitleText">Activate Experience</string>
39 48
     <string name="experienceRegistrationDescriptionText">Click the button below to enter the research experience you are participating in.</string>
40 49
     <string name="experienceRegistrationButtonText">Enter</string>
50
+    <string name="consentFileName">/consent-tania-app.pdf</string>
41 51
 </resources>

+ 1
- 1
build.gradle View File

@@ -7,7 +7,7 @@ buildscript {
7 7
         jcenter()
8 8
     }
9 9
     dependencies {
10
-        classpath 'com.android.tools.build:gradle:4.1.0'
10
+        classpath 'com.android.tools.build:gradle:4.1.1'
11 11
         classpath 'com.google.gms:google-services:4.3.3'
12 12
         
13 13