Commit 9b3438fb authored by Lückemeyer's avatar Lückemeyer
Browse files

Gemini added WordCount for boxes in memorize combo box

parent 8fb81ab9
......@@ -19,8 +19,6 @@ public interface CardDao {
/**
* Returns all cards whose most recent try_record for the given direction
* places them in the given Leitner box.
* Cards with no try_record for this direction are excluded here —
* use getUnattemptedForDirectionSync for those.
*/
@Query("SELECT c.* FROM card c " +
"JOIN ( " +
......@@ -41,9 +39,25 @@ public interface CardDao {
"ORDER BY c.volet_id ASC, c.id ASC")
List<Card> getByBoxSync(int box, String direction);
@Query("SELECT COUNT(DISTINCT c.id) FROM card c " +
"JOIN ( " +
" SELECT tr.leitner_state_id, ls.card_id, ls.box_number " +
" FROM try_record tr " +
" JOIN leitner_state ls ON ls.id = tr.leitner_state_id " +
" WHERE ls.direction = :direction " +
" AND tr.responded_at = ( " +
" SELECT MAX(tr2.responded_at) " +
" FROM try_record tr2 " +
" JOIN leitner_state ls2 ON ls2.id = tr2.leitner_state_id " +
" WHERE ls2.card_id = ls.card_id " +
" AND ls2.direction = :direction " +
" ) " +
") latest ON latest.card_id = c.id " +
"WHERE latest.box_number = :box")
int countByBoxSync(int box, String direction);
/**
* Cards that have never been attempted in the given direction.
* These implicitly belong to box 1 for that direction.
*/
@Query("SELECT * FROM card c " +
"WHERE NOT EXISTS ( " +
......@@ -53,4 +67,12 @@ public interface CardDao {
") " +
"ORDER BY c.volet_id ASC, c.id ASC")
List<Card> getUnattemptedForDirectionSync(String direction);
@Query("SELECT COUNT(*) FROM card c " +
"WHERE NOT EXISTS ( " +
" SELECT 1 FROM try_record tr " +
" JOIN leitner_state ls ON ls.id = tr.leitner_state_id " +
" WHERE ls.card_id = c.id AND ls.direction = :direction " +
")")
int countUnattemptedForDirectionSync(String direction);
}
package dev.lueckemeyer.vocoach.ui.memorize;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.*;
......@@ -10,6 +11,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import dev.lueckemeyer.vocoach.R;
import dev.lueckemeyer.vocoach.databinding.FragmentMemorizeBinding;
import dev.lueckemeyer.vocoach.db.VocabDatabase;
import dev.lueckemeyer.vocoach.db.dao.VoletWithContext;
......@@ -58,7 +60,7 @@ public class MemorizeFragment extends Fragment {
binding.toggleDirection.setChecked(false); // start on French
binding.toggleDirection.setOnCheckedChangeListener((btn, checked) -> {
boxDirection = checked ? "native_to_fr" : "fr_to_native";
if (!byVolet) loadTable(binding.spinnerSelection.getSelectedItemPosition());
if (!byVolet) refreshSpinner();
});
// ── Spinner selection triggers table refresh ──────────────────────────
......@@ -95,15 +97,22 @@ public class MemorizeFragment extends Fragment {
});
});
} else {
List<String> boxes = new ArrayList<>();
for (int i = 1; i <= 6; i++) boxes.add("Box " + i);
ArrayAdapter<String> adapter = new ArrayAdapter<>(
requireContext(),
android.R.layout.simple_spinner_item, boxes);
adapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
binding.spinnerSelection.setAdapter(adapter);
loadTable(0);
exec.execute(() -> {
List<BoxItem> boxes = new ArrayList<>();
for (int i = 1; i <= 6; i++) {
int count = db.cardDao().countByBoxSync(i, boxDirection);
if (i == 1) {
count += db.cardDao().countUnattemptedForDirectionSync(boxDirection);
}
boxes.add(new BoxItem("Box " + i, count));
}
requireActivity().runOnUiThread(() -> {
if (getContext() == null) return;
BoxAdapter adapter = new BoxAdapter(requireContext(), boxes);
binding.spinnerSelection.setAdapter(adapter);
loadTable(0);
});
});
}
}
......@@ -186,4 +195,43 @@ public class MemorizeFragment extends Fragment {
super.onDestroyView();
binding = null;
}
// ── Box Spinner Helpers ──────────────────────────────────────────────────
private static class BoxItem {
final String label;
final int count;
BoxItem(String label, int count) { this.label = label; this.count = count; }
@NonNull @Override public String toString() { return label; }
}
private class BoxAdapter extends ArrayAdapter<BoxItem> {
public BoxAdapter(@NonNull Context context, @NonNull List<BoxItem> items) {
super(context, R.layout.item_spinner_with_count, items);
}
@NonNull
@Override
public View getView(int pos, @Nullable View conv, @NonNull ViewGroup parent) {
return createView(pos, conv, parent);
}
@Override
public View getDropDownView(int pos, @Nullable View conv, @NonNull ViewGroup parent) {
return createView(pos, conv, parent);
}
private View createView(int pos, View conv, ViewGroup parent) {
if (conv == null) {
conv = LayoutInflater.from(getContext())
.inflate(R.layout.item_spinner_with_count, parent, false);
}
BoxItem item = getItem(pos);
if (item != null) {
((TextView) conv.findViewById(R.id.text_main)).setText(item.label);
((TextView) conv.findViewById(R.id.text_count)).setText(String.valueOf(item.count));
}
return conv;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:paddingStart="12dp"
android:paddingEnd="12dp">
<TextView
android:id="@+id/text_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/text_count"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp" />
<TextView
android:id="@+id/text_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:paddingStart="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</RelativeLayout>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment