Skip to content

Commit efe8d52

Browse files
committed
Added questionnaire answer viewer
Moved questionnaire buttons to the top for a more compact view. Added listing for all filled-out questionnaires (not only those which have a journal entry on the same day). Added viewer to see answers for filled-out questionnaires.
1 parent 29926e9 commit efe8d52

31 files changed

+1188
-336
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
android:roundIcon="@mipmap/ic_launcher_pure_round"
3030
android:supportsRtl="true"
3131
android:theme="@style/Theme.LucidSourceKit.Default">
32+
<activity
33+
android:name=".main.questionnaire.CompletedQuestionnaireViewerActivity"
34+
android:exported="false" />
3235
<activity
3336
android:name=".main.questionnaire.QuestionnaireEditorActivity"
3437
android:exported="false" />

app/src/main/java/com/bitflaker/lucidsourcekit/database/dreamjournal/entities/resulttables/DreamJournalEntry.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class DreamJournalEntry {
4242
@Ignore
4343
private final List<String> audioLocationsToDelete = new ArrayList<>();
4444

45+
@Ignore
46+
private int questionnaireCount = -1;
47+
4548
// Recording modifications
4649

4750
public void addAudioLocation(AudioLocation audioLocation) {
@@ -124,6 +127,14 @@ public void removeDreamType(String dreamType) {
124127
.ifPresent(type -> journalEntryHasTypes.remove(type));
125128
}
126129

130+
public int getQuestionnaireCount() {
131+
return questionnaireCount;
132+
}
133+
134+
public void setQuestionnaireCount(int count) {
135+
questionnaireCount = count;
136+
}
137+
127138
// Equality checking
128139

129140
@Override

app/src/main/java/com/bitflaker/lucidsourcekit/database/questionnaire/daos/CompletedQuestionnaireDao.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.room.OnConflictStrategy
77
import androidx.room.Query
88
import androidx.room.Update
99
import com.bitflaker.lucidsourcekit.database.questionnaire.entities.CompletedQuestionnaire
10+
import com.bitflaker.lucidsourcekit.database.questionnaire.entities.resulttables.CompletedQuestionnaireDetails
1011
import io.reactivex.rxjava3.core.Completable
1112
import io.reactivex.rxjava3.core.Single
1213

@@ -15,9 +16,15 @@ interface CompletedQuestionnaireDao {
1516
@Query("SELECT * FROM CompletedQuestionnaire ORDER BY id")
1617
fun getAll(): Single<List<CompletedQuestionnaire>>
1718

19+
@Query("SELECT cq.id, cq.questionnaireId, cq.answerDuration, cq.timestamp, q.title, q.description, q.colorCode, q.orderNr FROM CompletedQuestionnaire cq LEFT JOIN Questionnaire q ON cq.questionnaireId = q.id ORDER BY timestamp DESC")
20+
fun getAllDetails(): Single<List<CompletedQuestionnaireDetails>>
21+
1822
@Query("SELECT * FROM CompletedQuestionnaire WHERE id = :id")
1923
fun getById(id: Int): Single<CompletedQuestionnaire>
2024

25+
@Query("SELECT cq.id, cq.questionnaireId, cq.answerDuration, cq.timestamp, q.title, q.description, q.colorCode, q.orderNr FROM CompletedQuestionnaire cq LEFT JOIN Questionnaire q ON cq.questionnaireId = q.id WHERE cq.id = :id")
26+
fun getDetailsById(id: Int): Single<CompletedQuestionnaireDetails>
27+
2128
@Update
2229
fun update(entry: CompletedQuestionnaire): Completable
2330

@@ -35,4 +42,10 @@ interface CompletedQuestionnaireDao {
3542

3643
@Query("DELETE FROM CompletedQuestionnaire")
3744
fun deleteAll(): Completable
45+
46+
@Query("SELECT cq.id, cq.questionnaireId, cq.answerDuration, cq.timestamp, q.title, q.description, q.colorCode, q.orderNr FROM CompletedQuestionnaire cq LEFT JOIN Questionnaire q ON cq.questionnaireId = q.id WHERE timestamp >= :dayFrom AND timestamp < :dayTo")
47+
fun getByTimeFrame(dayFrom: Long, dayTo: Long): Single<List<CompletedQuestionnaireDetails>>
48+
49+
@Query("SELECT COUNT(*) FROM CompletedQuestionnaire WHERE timestamp >= :dayFrom AND timestamp < :dayTo")
50+
fun getQuestionnaireCount(dayFrom: Long, dayTo: Long): Single<Int>
3851
}

app/src/main/java/com/bitflaker/lucidsourcekit/database/questionnaire/daos/QuestionnaireAnswerDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ interface QuestionnaireAnswerDao {
1717
@Query("SELECT * FROM QuestionnaireAnswer WHERE completedQuestionnaireId = :completedQuestionnaireId AND questionId = :questionId")
1818
fun getById(completedQuestionnaireId: Int, questionId: Int): Single<QuestionnaireAnswer>
1919

20+
@Query("SELECT * FROM QuestionnaireAnswer WHERE completedQuestionnaireId = :completedQuestionnaireId")
21+
fun getAll(completedQuestionnaireId: Int): Single<List<QuestionnaireAnswer>>
22+
2023
@Update
2124
fun update(entry: QuestionnaireAnswer): Completable
2225

app/src/main/java/com/bitflaker/lucidsourcekit/database/questionnaire/daos/SelectedOptionsDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ interface SelectedOptionsDao {
1313
@Query("SELECT * FROM SelectedOptions ORDER BY completedQuestionnaireId, questionId, optionId")
1414
fun getAll(): Single<List<SelectedOptions>>
1515

16+
@Query("SELECT * FROM SelectedOptions WHERE completedQuestionnaireId = :completedQuestionnaireId AND questionId = :questionId ORDER BY completedQuestionnaireId, questionId, optionId")
17+
fun getById(completedQuestionnaireId: Int, questionId: Int): Single<List<SelectedOptions>>
18+
1619
@Insert
1720
fun insert(entry: SelectedOptions): Completable
1821

app/src/main/java/com/bitflaker/lucidsourcekit/database/questionnaire/entities/Question.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ data class Question(
4040

4141
@Ignore
4242
var options: MutableList<QuestionOptions>? = null
43+
44+
@Ignore
45+
var value: String? = null
4346
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.bitflaker.lucidsourcekit.database.questionnaire.entities.resulttables
2+
3+
data class CompletedQuestionnaireDetails(
4+
val id: Int,
5+
val questionnaireId: Int,
6+
val answerDuration: Long,
7+
val timestamp: Long,
8+
var title: String,
9+
var description: String?,
10+
var orderNr: Int,
11+
var colorCode: String?
12+
)

app/src/main/java/com/bitflaker/lucidsourcekit/main/alarms/RecyclerViewAdapterAlarms.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ public void onBindViewHolder(@NonNull MainViewHolderAlarms holder, int position)
7676
loadedItems.add(holder);
7777
loadedPositions.add(position);
7878
holder.binding.txtAlarmsTitle.setText(alarm.title);
79-
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) holder.binding.clAlarmContent.getLayoutParams();
79+
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) holder.binding.clQuestionnaire.getLayoutParams();
8080
params.setMargins(horizontalPadding, 0, horizontalPadding, 0);
81-
holder.binding.clAlarmContent.setLayoutParams(params);
81+
holder.binding.clQuestionnaire.setLayoutParams(params);
8282

8383
long alarmHours = TimeUnit.MILLISECONDS.toHours(alarm.alarmTimestamp);
8484
long alarmMinutes = TimeUnit.MILLISECONDS.toMinutes(alarm.alarmTimestamp) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(alarm.alarmTimestamp));

app/src/main/java/com/bitflaker/lucidsourcekit/main/dreamjournal/DreamJournalView.java

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bitflaker.lucidsourcekit.main.dreamjournal;
22

3+
import static android.app.Activity.RESULT_OK;
34
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
45

56
import android.app.Activity;
@@ -19,6 +20,7 @@
1920
import androidx.annotation.NonNull;
2021
import androidx.annotation.Nullable;
2122
import androidx.appcompat.app.AlertDialog;
23+
import androidx.appcompat.app.AppCompatActivity;
2224
import androidx.fragment.app.Fragment;
2325
import androidx.recyclerview.widget.LinearLayoutManager;
2426
import androidx.recyclerview.widget.RecyclerView;
@@ -29,13 +31,26 @@
2931
import com.bitflaker.lucidsourcekit.data.records.SortEntry;
3032
import com.bitflaker.lucidsourcekit.database.MainDatabase;
3133
import com.bitflaker.lucidsourcekit.database.dreamjournal.entities.resulttables.DreamJournalEntry;
34+
import com.bitflaker.lucidsourcekit.database.questionnaire.entities.CompletedQuestionnaire;
35+
import com.bitflaker.lucidsourcekit.database.questionnaire.entities.resulttables.CompletedQuestionnaireDetails;
36+
import com.bitflaker.lucidsourcekit.databinding.ActivityCompletedQuestionnaireViewerBinding;
3237
import com.bitflaker.lucidsourcekit.databinding.FragmentMainJournalBinding;
38+
import com.bitflaker.lucidsourcekit.databinding.SheetQuestionnaireListBinding;
39+
import com.bitflaker.lucidsourcekit.main.questionnaire.CompletedQuestionnaireViewerActivity;
40+
import com.bitflaker.lucidsourcekit.main.questionnaire.QuestionnaireEditorActivity;
41+
import com.bitflaker.lucidsourcekit.main.questionnaire.QuestionnaireView;
42+
import com.bitflaker.lucidsourcekit.main.questionnaire.RecyclerViewFilledOutQuestionnaires;
3343
import com.bitflaker.lucidsourcekit.utils.Tools;
44+
import com.google.android.material.bottomsheet.BottomSheetDialog;
3445
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
3546

47+
import java.text.DateFormat;
48+
import java.util.Calendar;
3649
import java.util.List;
50+
import java.util.stream.Collectors;
3751

3852
import io.reactivex.rxjava3.disposables.CompositeDisposable;
53+
import kotlin.Unit;
3954

4055
public class DreamJournalView extends Fragment {
4156
public ActivityResultLauncher<Intent> journalEditorActivityResultLauncher;
@@ -44,6 +59,10 @@ public class DreamJournalView extends Fragment {
4459
private DreamJournalEntry.EntryType autoOpenJournalTypeCreator = null;
4560
private CompositeDisposable compositeDisposable;
4661
private FragmentMainJournalBinding binding;
62+
private RecyclerViewFilledOutQuestionnaires questionnaireAdapter;
63+
private SheetQuestionnaireListBinding questionnaireSheetBinding;
64+
private ActivityResultLauncher<Intent> editorLauncher;
65+
private int entryIdToUpdateQuestionnaires = -1;
4766
private MainDatabase db;
4867
private boolean isOpen = false;
4968
private int sortBy = 0;
@@ -73,12 +92,44 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
7392

7493
db = MainDatabase.getInstance(getContext());
7594

95+
Activity activity = getActivity();
96+
editorLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
97+
Intent intent = result.getData();
98+
if (intent != null) {
99+
int id = intent.getIntExtra("COMPLETED_QUESTIONNAIRE_ID", -1);
100+
if (result.getResultCode() == RESULT_OK && id != -1) {
101+
if (entryIdToUpdateQuestionnaires != -1) {
102+
reloadEntryData(entryIdToUpdateQuestionnaires);
103+
}
104+
else {
105+
reloadEntryDataByCompleted(id);
106+
}
107+
if (questionnaireAdapter != null) {
108+
CompletedQuestionnaireDetails completed = db.getCompletedQuestionnaireDao().getDetailsById(id).blockingGet();
109+
questionnaireAdapter.addCompletedQuestionnaire(completed);
110+
activity.runOnUiThread(() -> {
111+
questionnaireSheetBinding.txtNoQuestionnairesTitle.setVisibility(View.GONE);
112+
questionnaireSheetBinding.txtNoQuestionnairesSubTitle.setVisibility(View.GONE);
113+
});
114+
}
115+
questionnaireAdapter = null;
116+
questionnaireSheetBinding = null;
117+
entryIdToUpdateQuestionnaires = -1;
118+
}
119+
}
120+
});
121+
76122
fabOpen = AnimationUtils.loadAnimation(getContext(), R.anim.add_open);
77123
fabClose = AnimationUtils.loadAnimation(getContext(),R.anim.add_close);
78124
rotateForward = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_forward);
79125
rotateBackward = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_backward);
80126

81127
compositeDisposable.add(db.getJournalEntryDao().getAll().subscribe(journalEntries -> {
128+
journalEntries.forEach(e -> {
129+
long dayFrom = Tools.getMidnightTime(e.journalEntry.timeStamp);
130+
long dayTo = dayFrom + 24 * 60 * 60 * 1000;
131+
e.setQuestionnaireCount(db.getCompletedQuestionnaireDao().getQuestionnaireCount(dayFrom, dayTo).blockingGet());
132+
});
82133
getActivity().runOnUiThread(() -> {
83134
recyclerViewAdapterDreamJournal = new RecyclerViewAdapterDreamJournal(getActivity(), this, journalEntries);
84135
setBasics();
@@ -88,14 +139,58 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
88139
setupResetFilterButton();
89140
handleItemCount(journalEntries.size());
90141
recyclerViewAdapterDreamJournal.setOnEntryCountChangedListener(this::handleItemCount);
142+
recyclerViewAdapterDreamJournal.setOnQuestionnaireAddClickListener(this::viewQuestionnaires);
91143
});
92144
}));
93145

94-
if(autoOpenJournalTypeCreator != null) {
146+
if (autoOpenJournalTypeCreator != null) {
95147
// TODO when an entry was created after the editor was opened by the alarm quick action, the list of entries in the MainViewer does not get updated
96148
showJournalCreator(autoOpenJournalTypeCreator);
97149
autoOpenJournalTypeCreator = null;
98150
}
151+
152+
binding.crdAllQuestionnaires.setOnClickListener(e -> {
153+
startActivity(new Intent(getContext(), CompletedQuestionnaireViewerActivity.class));
154+
// TODO: Add result listener to update all questionnaire counts for all journal entries at the dates where a new questionnaire was added
155+
});
156+
}
157+
158+
private void viewQuestionnaires(DreamJournalEntry entry) {
159+
long timestamp = entry.journalEntry.timeStamp;
160+
BottomSheetDialog bsd = new BottomSheetDialog(getContext(), R.style.BottomSheetDialogStyle);
161+
questionnaireSheetBinding = com.bitflaker.lucidsourcekit.databinding.SheetQuestionnaireListBinding.inflate(getLayoutInflater());
162+
bsd.setContentView(questionnaireSheetBinding.getRoot());
163+
164+
long dayFrom = Tools.getMidnightTime(timestamp);
165+
long dayTo = dayFrom + 24 * 60 * 60 * 1000;
166+
167+
questionnaireSheetBinding.txtQuestionnairesDate.setText(DateFormat.getDateInstance(DateFormat.MEDIUM).format(timestamp));
168+
169+
List<CompletedQuestionnaireDetails> completed = db.getCompletedQuestionnaireDao().getByTimeFrame(dayFrom, dayTo).blockingGet();
170+
171+
int emptyVisibility = completed.isEmpty() ? View.VISIBLE : View.GONE;
172+
questionnaireSheetBinding.txtNoQuestionnairesTitle.setVisibility(emptyVisibility);
173+
questionnaireSheetBinding.txtNoQuestionnairesSubTitle.setVisibility(emptyVisibility);
174+
175+
questionnaireSheetBinding.rcvQuestionnairesFilledOut.setLayoutManager(new LinearLayoutManager(getContext()));
176+
questionnaireAdapter = new RecyclerViewFilledOutQuestionnaires(getContext(), completed);
177+
questionnaireAdapter.setOnQuestionnaireClickListener(completedId -> {
178+
Intent intent = new Intent(getContext(), QuestionnaireEditorActivity.class);
179+
intent.putExtra("COMPLETED_QUESTIONNAIRE_ID", completedId);
180+
startActivity(intent);
181+
return Unit.INSTANCE;
182+
});
183+
questionnaireSheetBinding.rcvQuestionnairesFilledOut.setAdapter(questionnaireAdapter);
184+
185+
questionnaireSheetBinding.btnFillOutQuestionnaire.setOnClickListener(e -> {
186+
entryIdToUpdateQuestionnaires = entry.journalEntry.entryId;
187+
long dateTimeStamp = timestamp - Tools.getTimeOfDayMillis(timestamp);
188+
Intent intent = new Intent(getContext(), QuestionnaireView.class);
189+
intent.putExtra("USE_SPECIFIC_DATE", dateTimeStamp);
190+
editorLauncher.launch(intent);
191+
});
192+
193+
bsd.show();
99194
}
100195

101196
private void setBasics() {
@@ -163,6 +258,12 @@ private void setupFAB() {
163258
binding.btnAddJournalEntry.setOnClickListener(e -> animateFab());
164259
binding.fabText.setOnClickListener(e -> showJournalCreator(DreamJournalEntry.EntryType.PLAIN_TEXT));
165260
binding.fabForms.setOnClickListener(e -> showJournalCreator(DreamJournalEntry.EntryType.FORMS_TEXT));
261+
binding.fabQuestionnaire.setOnClickListener(e -> showQuestionnaireCreator());
262+
}
263+
264+
private void showQuestionnaireCreator() {
265+
animateFab();
266+
editorLauncher.launch(new Intent(getContext(), QuestionnaireView.class));
166267
}
167268

168269
private void handleItemCount(int itemCount) {
@@ -187,20 +288,24 @@ private void animateFab() {
187288
binding.btnAddJournalEntry.startAnimation(rotateForward);
188289
binding.fabText.startAnimation(fabClose);
189290
binding.fabForms.startAnimation(fabClose);
291+
binding.fabQuestionnaire.startAnimation(fabClose);
190292
Tools.animateBackgroundTint(binding.btnAddJournalEntry, colorOpenBackground, colorClosedBackground, 300);
191293
Tools.animateImageTint(binding.btnAddJournalEntry, colorOnOpenBackground, colorOnClosedBackground, 300);
192294
binding.fabText.setClickable(false);
193295
binding.fabForms.setClickable(false);
296+
binding.fabQuestionnaire.setClickable(false);
194297
isOpen=false;
195298
}
196299
else {
197300
binding.btnAddJournalEntry.startAnimation(rotateBackward);
198301
binding.fabText.startAnimation(fabOpen);
199302
binding.fabForms.startAnimation(fabOpen);
303+
binding.fabQuestionnaire.startAnimation(fabOpen);
200304
Tools.animateBackgroundTint(binding.btnAddJournalEntry, colorClosedBackground, colorOpenBackground, 300);
201305
Tools.animateImageTint(binding.btnAddJournalEntry, colorOnClosedBackground, colorOnOpenBackground, 300);
202306
binding.fabText.setClickable(true);
203307
binding.fabForms.setClickable(true);
308+
binding.fabQuestionnaire.setClickable(true);
204309
isOpen=true;
205310
}
206311
}
@@ -219,15 +324,30 @@ private void setupViewResultLauncher() {
219324
});
220325
}
221326

327+
private void reloadEntryDataByCompleted(int id) {
328+
CompletedQuestionnaire completed = db.getCompletedQuestionnaireDao().getById(id).blockingGet();
329+
List<Integer> idsToReload = recyclerViewAdapterDreamJournal.getEntriesByDate(completed.getTimestamp());
330+
List<DreamJournalEntry> entriesToReload = idsToReload.stream().map(this::getDreamJournalEntry).collect(Collectors.toList());
331+
recyclerViewAdapterDreamJournal.updateDataForEntry(entriesToReload, null);
332+
}
333+
222334
private void reloadEntryData(int entryId) {
223-
DreamJournalEntry entry = db.getJournalEntryDao().getEntryDataById(entryId).blockingGet();
224-
recyclerViewAdapterDreamJournal.updateDataForEntry(entry, insertedIndex -> {
335+
recyclerViewAdapterDreamJournal.updateDataForEntry(getDreamJournalEntry(entryId), insertedIndex -> {
225336
if (insertedIndex != -1) {
226337
binding.recyclerView.scrollToPosition(insertedIndex);
227338
}
228339
});
229340
}
230341

342+
@NonNull
343+
private DreamJournalEntry getDreamJournalEntry(int entryId) {
344+
DreamJournalEntry entry = db.getJournalEntryDao().getEntryDataById(entryId).blockingGet();
345+
long dayFrom = Tools.getMidnightTime(entry.journalEntry.timeStamp);
346+
long dayTo = dayFrom + 24 * 60 * 60 * 1000;
347+
entry.setQuestionnaireCount(db.getCompletedQuestionnaireDao().getQuestionnaireCount(dayFrom, dayTo).blockingGet());
348+
return entry;
349+
}
350+
231351
public void openJournalEntry(DreamJournalEntry entry, boolean tryResetFilters) {
232352
// Check if the entry complies with current filters otherwise reset filters
233353
if (tryResetFilters && !entry.compliesWithFilter(recyclerViewAdapterDreamJournal.getCurrentFilter())) {

0 commit comments

Comments
 (0)