Skip to content

Commit

Permalink
Implement saving of completed surveys to RoomD
Browse files Browse the repository at this point in the history
Signed-off-by: Amr Hossam <[email protected]>
  • Loading branch information
amrhossamdev committed Aug 19, 2024
1 parent 93fec4a commit 23361cc
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 58 deletions.
5 changes: 5 additions & 0 deletions onebusaway-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.apache.tools.ant.filters.ConcatFilter
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

repositories {
maven {
Expand Down Expand Up @@ -368,6 +369,10 @@ dependencies {
implementation "androidx.concurrent:concurrent-listenablefuture-callback:1.0.0-beta01"
implementation "androidx.core:core-ktx:1.7.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// RoomDB
implementation "androidx.room:room-runtime:2.5.0"
kapt "androidx.room:room-compiler:2.5.0"
implementation "androidx.room:room-ktx:2.5.0"

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.onebusaway.android.database

import androidx.room.Database
import androidx.room.RoomDatabase
import org.onebusaway.android.ui.survey.dao.StudiesDao
import org.onebusaway.android.ui.survey.dao.SurveysDao
import org.onebusaway.android.ui.survey.entity.Study
import org.onebusaway.android.ui.survey.entity.Survey

@Database(entities = [Study::class, Survey::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun studiesDao(): StudiesDao
abstract fun surveysDao(): SurveysDao
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,8 @@
public class SurveyLocalData {

private static final String PREFS_NAME = "survey_pref";
private static final String HASH_MAP_KEY = "survey_hash_map";
private static final String UUID_KEY = "my_uuid";

/**
* Saves the answered surveys to SharedPreferences.
* The HashMap contains survey IDs as keys and their completion status as boolean values.
*
* @param context The context used to access SharedPreferences.
* @param hashMap A HashMap where the keys are survey IDs and the values are booleans indicating completion status.
*/
public static void setCompletedSurveys(Context context, HashMap<String, Boolean> hashMap) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
JSONObject jsonObject = new JSONObject(hashMap);
String jsonString = jsonObject.toString();
editor.putString(HASH_MAP_KEY, jsonString);
editor.apply();
}

/**
* Retrieves the answered surveys from SharedPreferences as a HashMap.
* The map contains survey IDs as keys and their completion status as boolean values.
*
* @param context The context used to access SharedPreferences.
* @return A HashMap where the keys are survey IDs and the values are booleans indicating completion status.
*/
public static HashMap<String, Boolean> getCompletedSurveys(Context context) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String jsonString = prefs.getString(HASH_MAP_KEY, "{}");
HashMap<String, Boolean> hashMap = new HashMap<>();
try {
JSONObject jsonObject = new JSONObject(jsonString);
Iterator<String> keys = jsonObject.keys();
while (keys.hasNext()) {
String key = keys.next();
boolean value = jsonObject.getBoolean(key);
hashMap.put(key, value);
}
} catch (JSONException e) {
e.printStackTrace();
}
return hashMap;
}

public static void saveUserUUID(Context context, UUID uuid) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.onebusaway.android.io.request.survey.model.SubmitSurveyResponse;
import org.onebusaway.android.io.request.survey.submit.ObaSubmitSurveyRequest;
import org.onebusaway.android.io.request.survey.submit.SubmitSurveyRequestListener;
import org.onebusaway.android.ui.survey.utils.SurveyDbHelper;
import org.onebusaway.android.ui.survey.utils.SurveyUtils;
import org.onebusaway.android.ui.survey.utils.SurveyViewUtils;
import org.onebusaway.android.ui.survey.activities.SurveyWebViewActivity;
Expand Down Expand Up @@ -292,7 +293,8 @@ private void handleExternalSurvey() {
* Mark current survey as done
*/
public void handleCompleteSurvey() {
SurveyUtils.markSurveyAsCompleted(context, String.valueOf(curSurveyID));
StudyResponse.Surveys currentSurvey = mStudyResponse.getSurveys().get(curSurveyIndex);
SurveyDbHelper.markSurveyAsCompleted(context, currentSurvey);
// Remove the hero question view
if (isVisibleOnStops) arrivalsList.removeHeaderView(surveyView);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.onebusaway.android.ui.survey.dao

import androidx.room.*
import org.onebusaway.android.ui.survey.entity.Study

@Dao
interface StudiesDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertStudy(study: Study): Long

@Update
suspend fun updateStudy(study: Study)

@Delete
suspend fun deleteStudy(study: Study)

@Query("SELECT * FROM studies WHERE study_id = :studyId")
suspend fun getStudyById(studyId: Int): Study?

@Query("SELECT * FROM studies")
suspend fun getAllStudies(): List<Study>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.onebusaway.android.ui.survey.dao

import androidx.room.*
import org.onebusaway.android.ui.survey.entity.Survey

@Dao
interface SurveysDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSurvey(survey: Survey): Long

@Update
suspend fun updateSurvey(survey: Survey)

@Delete
suspend fun deleteSurvey(survey: Survey)

@Query("SELECT *FROM surveys")
suspend fun getAllSurveys():List<Survey>

@Query("SELECT * FROM surveys WHERE survey_id = :surveyId")
suspend fun getSurveyById(surveyId: Int): Survey?

@Query("SELECT * FROM surveys WHERE study_id = :studyId")
suspend fun getSurveysByStudyId(studyId: Int): List<Survey>

@Query("SELECT COUNT(*) > 0 FROM surveys WHERE survey_id = :surveyId")
suspend fun isSurveyIdExists(surveyId: Int): Boolean

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.onebusaway.android.ui.survey.entity

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "studies")
data class Study(
@PrimaryKey val study_id: Int,
val name: String,
val description: String,
val is_subscribed: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.onebusaway.android.ui.survey.entity

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey

@Entity(
tableName = "surveys", foreignKeys = [ForeignKey(
entity = Study::class,
parentColumns = ["study_id"],
childColumns = ["study_id"],
onDelete = ForeignKey.CASCADE
)]
)
data class Survey(
@PrimaryKey val survey_id: Int, val study_id: Int, val name: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.onebusaway.android.ui.survey.repository

import android.content.Context
import androidx.room.Room
import org.onebusaway.android.database.AppDatabase
import org.onebusaway.android.ui.survey.entity.Study
import org.onebusaway.android.ui.survey.entity.Survey


class SurveyRepository(context: Context) {
private val db: AppDatabase = Room.databaseBuilder(
context.applicationContext, AppDatabase::class.java, "study-survey-db"
).build()

private val studiesDao = db.studiesDao()
private val surveysDao = db.surveysDao()

suspend fun addOrUpdateStudy(study: Study) {
val existingStudy = studiesDao.getStudyById(study.study_id)
if (existingStudy == null) {
studiesDao.insertStudy(study)
} else {
studiesDao.updateStudy(study)
}
}

suspend fun getAllStudies(): List<Study> {
return studiesDao.getAllStudies()
}

suspend fun addSurvey(survey: Survey) {
surveysDao.insertSurvey(survey)
}

suspend fun getSurveysForStudy(studyId: Int): List<Survey> {
return surveysDao.getSurveysByStudyId(studyId)
}

suspend fun checkSurveyCompleted(surveyId: Int): Boolean {
return surveysDao.isSurveyIdExists(surveyId)
}

suspend fun getAllSurveys(): List<Survey> {
return surveysDao.getAllSurveys();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.onebusaway.android.ui.survey.utils

import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.onebusaway.android.io.request.survey.model.StudyResponse
import org.onebusaway.android.ui.survey.entity.Study
import org.onebusaway.android.ui.survey.entity.Survey
import org.onebusaway.android.ui.survey.repository.SurveyRepository

class SurveyDbHelper {
companion object {
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

@JvmStatic
fun markSurveyAsCompleted(context: Context, survey: StudyResponse.Surveys) {
val surveyRepo = SurveyRepository(context)

val newStudy = Study(
survey.study.id, survey.study.name, survey.study.description, true
)
val newSurvey = Survey(survey.id, survey.study.id, survey.name)
coroutineScope.launch {
try {
surveyRepo.addOrUpdateStudy(newStudy)
surveyRepo.addSurvey(newSurvey)
Log.d("All Saved Surveys", surveyRepo.getAllSurveys().toString());
} catch (e: Exception) {
e.printStackTrace()
}
}
}

@JvmStatic
fun isSurveyCompleted(context: Context, surveyId: Int): Boolean {
val surveyRepo = SurveyRepository(context)
return runBlocking {
try {
surveyRepo.checkSurveyCompleted(surveyId)
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.onebusaway.android.ui.survey.SurveyLocalData;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

Expand Down Expand Up @@ -83,9 +82,6 @@ public static String getTextInputAnswer(View surveyView) {
* @return The zero-based index of the current survey, or -1 if all surveys are completed or filtered out.
*/
public static Integer getCurrentSurveyIndex(StudyResponse studyResponse, Context context, Boolean isVisibleOnStop, ObaStop currentStop) {
// Map of completed surveys with survey IDs as keys and completion status as values
HashMap<String, Boolean> completedSurveysMap = SurveyLocalData.getCompletedSurveys(context);

List<StudyResponse.Surveys> surveys = studyResponse.getSurveys();

// Iterate through the surveys to find the first uncompleted one
Expand All @@ -97,7 +93,7 @@ public static Integer getCurrentSurveyIndex(StudyResponse studyResponse, Context
List<String> visibleRouteList = surveys.get(index).getVisible_route_list();

// Skip if there is not questions
if(surveys.get(index).getQuestions().isEmpty()) continue;
if (surveys.get(index).getQuestions().isEmpty()) continue;

// Skip this survey if it shouldn't be shown on either map or stops
if (!showQuestionOnStops && !showQuestionOnMaps) {
Expand All @@ -116,8 +112,12 @@ public static Integer getCurrentSurveyIndex(StudyResponse studyResponse, Context
if (!showQuestionOnMaps) continue;
}

boolean isSurveyCompleted = SurveyDbHelper.isSurveyCompleted(context,surveys.get(index).getId());

Log.d("isSurveyCompleted",isSurveyCompleted + " ");

// Return the index if the survey is uncompleted
if (completedSurveysMap.get(surveys.get(index).getId().toString()) == null) {
if (!isSurveyCompleted) {
return index;
}
}
Expand All @@ -128,13 +128,13 @@ public static Integer getCurrentSurveyIndex(StudyResponse studyResponse, Context
/**
* Determines whether to show a survey for the given stop based on the provided visible stops and routes lists.
*
* @param currentStop The current stop for which the survey visibility is being checked.
* @param currentStop The current stop for which the survey visibility is being checked.
* @param visibleStopsList A list of stop IDs where the survey should be shown. Can be null.
* @param visibleRouteList A list of route IDs where the survey should be shown. Can be null.
* @return true if the survey should be shown for the current stop, otherwise false.
*/
private static boolean showSurvey(ObaStop currentStop, List<String> visibleStopsList, List<String> visibleRouteList) {
if(currentStop == null || currentStop.getId() == null) return false;
if (currentStop == null || currentStop.getId() == null) return false;
// If both visibleStopsList and visibleRouteList are null, show the survey by default.
if (visibleRouteList == null && visibleStopsList == null) {
return true;
Expand All @@ -158,13 +158,6 @@ private static boolean showSurvey(ObaStop currentStop, List<String> visibleStops
return false;
}

public static void markSurveyAsCompleted(Context context, String surveyID) {
HashMap<String, Boolean> completedSurveys = SurveyLocalData.getCompletedSurveys(context);
completedSurveys.put(surveyID, true);
// Save to the local storage
SurveyLocalData.setCompletedSurveys(context, completedSurveys);
}

/**
* Extracts answers from a question view and constructs a JSON array as a request body.
*
Expand Down

0 comments on commit 23361cc

Please sign in to comment.